﻿using System;
using System.Security.Permissions;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text;
using System.Collections;
using System.Threading;
using System.Globalization;
using System.Runtime.InteropServices;
#if !PocketPC || DesignTime
using System.ComponentModel;
#endif
#if !PocketPC || Framework20
using Microsoft.Win32;
#endif

using GeoFramework;
using GeoFramework.Gps;
using GeoFramework.Gps.Emulators;
using GeoFramework.IO.Serial;
using GeoFramework.IO.Bluetooth;
#if !PocketPC
using GeoFramework.IO.Usb;
#endif

namespace GeoFramework.IO
{

	/// <summary>Represents information about GPS devices on the local computer.</summary>
	/// <remarks>
	/// 	<para>This class encapsulates all information available regarding GPS devices. This
	///     class is also responsible for performing automatic device detection services, as
	///     well as providing protocol-independent notification of GPS data such as the current
	///     position, altitude and bearing.</para>
	/// 	<para>This class is primarily used as an entry point for automatic GPS device
	///     detection. Well-written applications will issue a call to the
	///     <strong>Detect</strong> method to begin searching for GPS devices in the
	///     background. By doing this early, such as when an application first begins to
	///     execute, the chances improve that a GPS device will be found before a device is
	///     actually needed. For an example on device detection, refer to the
	///     <strong>Detect</strong> method.</para>
	/// 	<para>More advanced developers who are working with different GPS data protocols
	///     such as Garmin®, Trimble® and SiRF®, will need to access this class more frequently
	///     in order to expand automatic device detection to include other protocols.</para>
	/// </remarks>
	/// <example>
	///     This example uses the <strong>Devices</strong> class to begin automatic GPS device
	///     detection for all known devices. Detection is done at the start of the application,
	///     and detection completes in the background. Performing detection as shown below is
	///     recommended for better performance. 
	///     <code lang="VB" title="[New Example]">
	/// ' The main entry point For the application.
	/// Shared Sub Main() 
	///     ' Start device detection In the background
	///     GeoFramework.IO.Devices.Detect()
	///     
	///     ' And run the application As usual
	///     Application.Run(New Form1())
	/// End Sub
	///     </code>
	/// 	<code lang="CS" title="[New Example]">
	/// // The main entry point for the application.
	/// static void Main() 
	/// {
	///     // Start device detection in the background
	///     GeoFramework.IO.Devices.Detect();
	///     
	///     // And run the application as usual
	///     Application.Run(new Form1());
	/// }
	///     </code>
	/// </example>
#if !PocketPC
    [RegistryPermission(SecurityAction.Assert, Read = @"HKEY_LOCAL_MACHINE\Software\WIDCOMM\BTConfig\Applications")]
    [RegistryPermission(SecurityAction.Assert, Read = @"HKEY_CURRENT_USER\Software\WIDCOMM\BTConfig\Applications")]
    [RegistryPermission(SecurityAction.Assert, Read = @"HKEY_LOCAL_MACHINE\Software\WIDCOMM\Connections")]
    [RegistryPermission(SecurityAction.Assert, Read = @"HKEY_CURRENT_USER\Software\WIDCOMM\Connections")]
    [RegistryPermission(SecurityAction.Assert, Read = @"HKEY_LOCAL_MACHINE\Software\WIDCOMM\BTConfig\AutoConnect")]
    [RegistryPermission(SecurityAction.Assert, Read = @"HKEY_CURRENT_USER\Software\GeoFrameworks\GPS.NET\2.0")]
    [RegistryPermission(SecurityAction.Assert, Write = @"HKEY_CURRENT_USER\Software\GeoFrameworks\GPS.NET\2.0")]
    [RegistryPermission(SecurityAction.Assert, Read = @"HKEY_LOCAL_MACHINE\Software\Microsoft\Bluetooth\Device")]
    [RegistryPermission(SecurityAction.Assert, Read = @"HKEY_LOCAL_MACHINE\Software\Microsoft\Bluetooth\Serial\Ports")]
    [RegistryPermission(SecurityAction.Assert, Read = @"HKEY_LOCAL_MACHINE\Drivers\Active")]
#endif
	public
#if Framework20
 static
#else
		sealed
#endif
		class Devices
    {
        #region Private Variables

        internal static ArrayList pSerialDevices = new ArrayList(1);
		internal static ArrayList pSerialGpsDevices = new ArrayList(1);
		internal static ArrayList pSerialBaudRates = new ArrayList(2);
		internal static ArrayList pBluetoothDevices = new ArrayList(1);
		internal static ArrayList pBluetoothGpsDevices = new ArrayList(1);
		internal static ArrayList pVirtualGpsDevices = new ArrayList(1);
		internal static ArrayList pGpsDevices = new ArrayList(1);
		internal static ArrayList pInterpreters = new ArrayList(1);
        internal static ArrayList pAvoidedSerialPorts = new ArrayList(1);
#if !PocketPC
		internal static ArrayList pUsbDevices = new ArrayList(1);
        internal static ArrayList pUsbGpsDevices = new ArrayList(1);
#endif
		private static Thread DetectionThread;
		private static ThreadPriority pDetectionThreadPriority = ThreadPriority.BelowNormal;
		private static bool pIsDetectionInProgress;
		private static bool pIsDetectionComplete;
		private static bool pIsOnlyFirstDeviceDetected;
        private static bool pIsExhaustiveDetectionEnabled;

        private static bool _IsSerialDeviceQueriedBeforeReading = true;

#if Smartphone && !Framework20
		// On Compact Framework 1.0 Smartphone, a WaitHandle is needed to see if
        // a Bluetooth phone has been discovered.
		private static ManualResetEvent DiscoveryWaitHandle = new ManualResetEvent(false);
#endif
		private static ManualResetEvent DetectionWaitHandle = new ManualResetEvent(false);
		private static ManualResetEvent DeviceDetectedWaitHandle = new ManualResetEvent(false);
		private static DateTime pCurrentUtcDateTime = DateTime.UtcNow;
		private static Distance pCurrentAltitude = Distance.Empty;
		private static Speed pCurrentSpeed = Speed.Empty;
		private static SatelliteCollection pCurrentSatellites = new SatelliteCollection();
		private static Azimuth pCurrentBearing = Azimuth.Empty;
		private static Position pCurrentPosition = Position.Empty;
		private static bool pCurrentlyFixed;
		private static bool pIsDeviceNeededImmediately;
		private static DilutionOfPrecision pCurrentMeanDilutionOfPrecision = DilutionOfPrecision.Maximum;
		private static DilutionOfPrecision pCurrentHorizontalDilutionOfPrecision = DilutionOfPrecision.Maximum;
		private static DilutionOfPrecision pCurrentVerticalDilutionOfPrecision = DilutionOfPrecision.Maximum;
		private static TimeSpan pDeviceDetectionTimeout = TimeSpan.FromSeconds(60);
		private static TimeSpan pDeviceDetectionTimeoutPerDevice = TimeSpan.FromSeconds(10);
		private static DevicesShutdownSignal pShutdownSignal;
        private static bool pAllowSerialConnections;
        private static bool pAllowBluetoothConnections = true;
#if !PocketPC
        private static bool pAllowUsbConnections = true;
#endif
		private static bool pIsDeviceCacheEnabled = true;
		private static bool pIsClockSynchronizationEnabled;
		private static int pMaximumSerialPortToScan = 20;
#if PocketPC 
		private static SharedGpsDevice pSharedGpsDevice = new SharedGpsDevice();
		private static bool pIsSharedGpsDeviceAutomaticallyConfigured = true;
		private static bool pAllowSharedGpsDeviceConnections = true;
#endif
#if !PocketPC || DesignTime
		private const int MaximumGracefulShutdownTime = 500;
#elif PocketPC && Framework20
		private const int MaximumGracefulShutdownTime = 500;
#endif
		private static Device pLastUsedGpsDevice;
		private static CommunicationPort pBluetoothVirtualSerialPort = CommunicationPort.Unknown;

        #endregion

        #region Constructors

        static Devices()
        {
            pShutdownSignal = new DevicesShutdownSignal();

            // Modify default settings depending on the platform
            switch (Platform.HostPlatformID)
            {
                case HostPlatformID.Desktop:
                    // Bluetooth connections are allowed on desktops
                    pAllowBluetoothConnections = true;

                    // Serial connections are allowed on desktops
                    pAllowSerialConnections = true;

                    // On desktops, we can safely "spam" COM: ports to find a GPS device
                    // which has not registered itself properly in the registry.
                    pIsExhaustiveDetectionEnabled = true;
                    break;
                case HostPlatformID.PocketPC:
                    // Serial connections are allowed on PocketPC
                    pAllowSerialConnections = true;

#if PocketPC
                    // On PocketPC systems, we can look for the Hewlett-Packard HW651x
                    // Is it supported?
                    if (HewlettPackard.Ipaq.IsSupported)
                    {
                        // Yes.  The HW651x series has a built-in GPS device on COM7:.
                        // To speed up detection, disallow Bluetooth scans as well as
                        // "spamming" COM0:-COM20: since we already know where the device
                        // is.  We also won't need to cache previously-detected device
                        // information.
                        pAllowBluetoothConnections = false;
                        pIsExhaustiveDetectionEnabled = false;
                        pIsDeviceCacheEnabled = false;
                    }
                    else
                    {
                        // Bluetooth connections are allowed on PocketPC
                        pAllowBluetoothConnections = true;

                        // The GPS Intermediate Driver is supported on PocketPC
                        pAllowSharedGpsDeviceConnections = true;

                        // On PocketPC, we can safely "spam" COM: ports to find a GPS device
                        // which has not registered itself properly in the registry.
                        pIsExhaustiveDetectionEnabled = true;
                    }
#endif
                    break;
                case HostPlatformID.WindowsCE:
                    // Bluetooth connections are allowed on Smartphone
                    pAllowBluetoothConnections = true;

                    // Serial connections are not allowed on Smartphone
                    pAllowSerialConnections = false;

#if PocketPC
                    // The GPS Intermediate Driver is not supported on Smartphone
                    pAllowSharedGpsDeviceConnections = false;
#endif

                    // On WinCE 4.2, we can safely "spam" COM: ports to find a GPS device
                    // which has not registered itself properly in the registry.
                    pIsExhaustiveDetectionEnabled = true;

                    break;
                case HostPlatformID.Smartphone:
                    // Bluetooth connections are allowed on Smartphone
                    pAllowBluetoothConnections = true;

                    // Serial connections are not allowed on Smartphone
                    pAllowSerialConnections = false;

#if PocketPC
                    // The GPS Intermediate Driver is not supported on Smartphone
                    pAllowSharedGpsDeviceConnections = false;
#endif

                    // On Smartphone, we can NOT safely "spam" COM: ports to find a GPS device
                    // because this leads to crashing the phone! (The phone will suddenly turn itself off)
                    pIsExhaustiveDetectionEnabled = false;

                    break;
            }

            // Set the list of serial baud rates to scan
            lock (pSerialBaudRates.SyncRoot)
            {
                pSerialBaudRates.Add(BaudRate.Baud57600);
                pSerialBaudRates.Add(BaudRate.Baud38400);
                pSerialBaudRates.Add(BaudRate.Baud9600);
                pSerialBaudRates.Add(BaudRate.Baud4800);
            }

            //#if PocketPC && !Smartphone

            //            /* The Hewlett-Packard 6515 has an integrated GPS device.  The problem
            //             * with the 6515 is that calls to Bluetooth connections cause a system
            //             * message box to be displayed.  So, disable Bluetooth connections on
            //             * the 651x series by default.
            //             */
            //            if (Environment.OSVersion.Platform == PlatformID.WinCE)
            //            {
            //                if (HewlettPackard.Ipaq.IsSupported)
            //                {
            //                    pAllowBluetoothConnections = false;
            //                    pIsExhaustiveDetectionEnabled = false;
            //                    pIsDeviceCacheEnabled = false;
            //                }
            //            }
            //#endif

            //#if PocketPC
            //            // Also see if we're on WM5 and have multiplexing enabled
            //            if (Environment.OSVersion.Platform == PlatformID.WinCE)
            //            {
            //                // Is the intermediate driver enabled?
            //                if (SharedGpsDevice.IsSupported)
            //                {
            //                    // If it's enabled and fully configured, use it!
            //                    if (pSharedGpsDevice.IsEnabled 
            //                        && pSharedGpsDevice.SourceDevice != null
            //                        && pSharedGpsDevice.SharedPortName != null)
            //                    {
            //                        pAllowBluetoothConnections = false;
            //                        pIsDeviceCacheEnabled = false;
            //#if !Smartphone
            //                        pIsExhaustiveDetectionEnabled = false;
            //#endif
            //                    }
            //                }
            //            }
            //#endif

            #region Determine the Bluetooth Virtual Serial Port

            // Now look for other possible areas
            RegistryKey Drivers = null;
            try
            {
                switch (Platform.HostPlatformID)
                {
                    case HostPlatformID.Smartphone:
                    case HostPlatformID.PocketPC:
                        // PocketPC's and Smartphones have settings under HKLM
                        Drivers = Registry.LocalMachine.OpenSubKey(@"Software\WIDCOMM\BTConfig\Applications", false);
                        break;
                    case HostPlatformID.Desktop:
                        // Desktops have settings under HKCU
                        Drivers = Registry.CurrentUser.OpenSubKey(@"Software\WIDCOMM\BTConfig\Applications", false);
                        break;
                    case HostPlatformID.WindowsCE:
                        // Not supported?
                        break;
                }

                if (Drivers != null)
                {
                    // Analyze all sub-keys for COM ports
                    foreach (string subKey in Drivers.GetSubKeyNames())
                    {
                        // Look for a  GUID of {00001101-0000-1000-8000-00805F9B34FB}
                        string GUID = null;
                        string ComPortNumber = null;
                        string Name = null;
                        // Look for a value called "Name"
                        RegistryKey SubKey = Drivers.OpenSubKey(subKey);
                        if (SubKey != null)
                        {
                            try
                            {
                                GUID = SubKey.GetValue("GUID") as string;
                                Name = SubKey.GetValue("Name") as string;
                                // If this is the right GUID, set the port
                                if (GUID == "{00001101-0000-1000-8000-00805F9B34FB}" && Name == "Generic Serial")
                                {
                                    ComPortNumber = Convert.ToString(SubKey.GetValue("ComPortNumber"));

#if PocketPC && !Framework20		
									// Use reflection (ARGH!) to get the value
									switch(Convert.ToInt32(ComPortNumber))
									{
										case 0:
											pBluetoothVirtualSerialPort = CommunicationPort.Com0;
											break;
										case 1:
											pBluetoothVirtualSerialPort = CommunicationPort.Com1;
											break;
										case 2:
											pBluetoothVirtualSerialPort = CommunicationPort.Com2;
											break;
										case 3:
											pBluetoothVirtualSerialPort = CommunicationPort.Com3;
											break;
										case 4:
											pBluetoothVirtualSerialPort = CommunicationPort.Com4;
											break;
										case 5:
											pBluetoothVirtualSerialPort = CommunicationPort.Com5;
											break;
										case 6:
											pBluetoothVirtualSerialPort = CommunicationPort.Com6;
											break;
										case 7:
											pBluetoothVirtualSerialPort = CommunicationPort.Com7;
											break;
										case 8:
											pBluetoothVirtualSerialPort = CommunicationPort.Com8;
											break;
										case 9:
											pBluetoothVirtualSerialPort = CommunicationPort.Com9;
											break;
										case 10:
											pBluetoothVirtualSerialPort = CommunicationPort.Com10;
											break;
										case 11:
											pBluetoothVirtualSerialPort = CommunicationPort.Com11;
											break;
										case 12:
											pBluetoothVirtualSerialPort = CommunicationPort.Com12;
											break;
										case 13:
											pBluetoothVirtualSerialPort = CommunicationPort.Com13;
											break;
										case 14:
											pBluetoothVirtualSerialPort = CommunicationPort.Com14;
											break;
										case 15:
											pBluetoothVirtualSerialPort = CommunicationPort.Com15;
											break;
										case 16:
											pBluetoothVirtualSerialPort = CommunicationPort.Com16;
											break;
										case 17:
											pBluetoothVirtualSerialPort = CommunicationPort.Com17;
											break;
										case 18:
											pBluetoothVirtualSerialPort = CommunicationPort.Com18;
											break;
										case 19:
											pBluetoothVirtualSerialPort = CommunicationPort.Com19;
											break;
										case 20:
											pBluetoothVirtualSerialPort = CommunicationPort.Com20;
											break;
										case 21:
											pBluetoothVirtualSerialPort = CommunicationPort.Com21;
											break;
										case 22:
											pBluetoothVirtualSerialPort = CommunicationPort.Com22;
											break;
										case 23:
											pBluetoothVirtualSerialPort = CommunicationPort.Com23;
											break;
										case 24:
											pBluetoothVirtualSerialPort = CommunicationPort.Com24;
											break;
										case 25:
											pBluetoothVirtualSerialPort = CommunicationPort.Com25;
											break;
										case 26:
											pBluetoothVirtualSerialPort = CommunicationPort.Com26;
											break;
										case 27:
											pBluetoothVirtualSerialPort = CommunicationPort.Com27;
											break;
										case 28:
											pBluetoothVirtualSerialPort = CommunicationPort.Com28;
											break;
										case 29:
											pBluetoothVirtualSerialPort = CommunicationPort.Com29;
											break;
										case 30:
											pBluetoothVirtualSerialPort = CommunicationPort.Com30;
											break;
									}
#else
                                    pBluetoothVirtualSerialPort = (CommunicationPort)Enum.Parse(typeof(CommunicationPort), ComPortNumber.ToString(), true);
#endif
                                    break;
                                }
                            }
                            catch
                            {
                                continue;
                            }
                            finally
                            {
                                if (SubKey != null)
                                    SubKey.Close();
                            }
                        }
                    }
                }
            }
            catch
            {
                // System security may be preventing access
            }
            finally
            {
                if (Drivers != null)
                {
                    Drivers.Close();
                }
            }

            #endregion

            // Refresh serial devices and Bluetooth
            //RefreshUsbDevices();
            RefreshSerialDevices();
            RefreshBluetoothDevices();

            // Which is the last-used GPS device?
            #region Get the last-known GPS device (if any)

            /* Let's see which device was the last one to get picked up. */
            RegistryKey BestDeviceKey = null;
            try
            {
                BestDeviceKey = Registry.CurrentUser.OpenSubKey(@"Software\GeoFrameworks\GPS.NET\2.0", true);
                if (BestDeviceKey != null)
                {
                    // Get the name of the device
                    string DeviceName = BestDeviceKey.GetValue("Last Used Device") as string;

                    // Look up the device to get the information
                    if (DeviceName != null)
                    {
                        RegistryKey LastDeviceKey = null;
                        try
                        {
                            LastDeviceKey = Registry.CurrentUser.OpenSubKey(@"Software\GeoFrameworks\GPS.NET\2.0\" + DeviceName, true);
                            if (LastDeviceKey != null)
                            {
                                // Is it a serial device
                                if (DeviceName.IndexOf(@"Serial Devices") != -1 && pAllowSerialConnections)
                                {
                                    #region Last-Known device is a Serial Device


                                    // Get the port index
                                    string PortName = LastDeviceKey.GetValue("Port Name") as string;
                                    if (PortName != null)
                                    {
                                        // Locate a serial device with the same name
                                        foreach (SerialDevice device in pSerialDevices)
                                        {
                                            // Are we skipping Bluetooth connections?
                                            if (device.PortName == PortName)
                                            {
                                                pLastUsedGpsDevice = device;
                                                device.SetIsLastUsedGpsDevice(true);
                                                break;
                                            }
                                        }
                                    }


                                    #endregion
                                }
                                else if (DeviceName.IndexOf("Bluetooth Devices") != -1 && pAllowBluetoothConnections)
                                {
                                    #region Last-Known device is a Bluetooth device

                                    string Address = LastDeviceKey.GetValue("Address") as string;
                                    if (Address != null)
                                    {
                                        foreach (BluetoothDevice device in pBluetoothDevices)
                                        {
                                            if (device.Address.ToString() == Address)
                                            {
                                                pLastUsedGpsDevice = device;
                                                device.SetIsLastUsedGpsDevice(true);
                                                break;
                                            }
                                        }
                                    }

                                    #endregion
                                }
#if !PocketPC
                                else if (DeviceName.IndexOf("USB Devices") != -1 && pAllowUsbConnections)
                                {
                                    #region Last-Known device is a USB device

                                    string Path = LastDeviceKey.GetValue("Path") as string;
                                    if (Path != null)
                                    {
                                        foreach (UsbDevice device in pUsbDevices)
                                        {
                                            if (device.Path.Equals(Path))
                                            {
                                                pLastUsedGpsDevice = device;
                                                device.SetIsLastUsedGpsDevice(true);
                                                break;
                                            }
                                        }
                                    }

                                    #endregion
                                }
#else
                                else if (DeviceName.IndexOf("Shared GPS Device") != -1 && pAllowSharedGpsDeviceConnections)
                                {
                                    #region Last-Known device is the GPS Intermediate Driver

                                    pLastUsedGpsDevice = pSharedGpsDevice;
                                    pSharedGpsDevice.SetIsLastUsedGpsDevice(true);
                                }

                                    #endregion
#endif
                            }
                        }
                        catch
                        {
                        }
                        finally
                        {
                            if (LastDeviceKey != null)
                                LastDeviceKey.Close();
                        }
                    }
                }
            }
            catch
            {
                // Ignore errors during device hints
            }
            finally
            {
                if (BestDeviceKey != null)
                    BestDeviceKey.Close();
            }

            #endregion
        }

        #endregion

        #region Finalizer (via shutdown signal)

        /// <summary>
		/// Cancels device detection and shuts down GPS device information services.
		/// </summary>
		/// <remarks>
		/// This method will forcefully dispose of any resources used
		/// to enumerate and manage known GPS devices.  This method
		/// should only be called when the application is shutting down
		/// because it leaves the class in an unusable state.  On Desktop 
		/// Frameworks and Visual Studio 2005, device detection is cancelled
		/// if it is in progress when this method is called.  This method is
		/// called automatically for Visual Studio 2005 applications, as well
		/// as Desktop Framework applications.
		/// </remarks>
		public static void Dispose()
		{
			lock (pSerialDevices.SyncRoot)
			{
				pSerialDevices.Clear();
			}
			lock (pSerialGpsDevices.SyncRoot)
			{
				pSerialGpsDevices.Clear();
			}
			lock (pBluetoothDevices.SyncRoot)
			{
				pBluetoothDevices.Clear();
			}
			lock (pBluetoothGpsDevices.SyncRoot)
			{
				pBluetoothGpsDevices.Clear();
			}
#if !PocketPC
            lock (pUsbDevices.SyncRoot)
            {
                pUsbDevices.Clear();
            }
            lock (pUsbGpsDevices.SyncRoot)
            {
                pUsbGpsDevices.Clear();
            }
#endif
			lock (pGpsDevices.SyncRoot)
			{
				pGpsDevices.Clear();
			}
			lock (pInterpreters.SyncRoot)
			{
				pInterpreters.Clear();
			}

#if !PocketPC || DesignTime || (PocketPC && Framework20)
            // Shut down the detection thread
            try
            {
                if (DetectionThread != null && !DetectionThread.Join(MaximumGracefulShutdownTime))
                    DetectionThread.Abort();
            }
            catch
            {
            }
#else
			// Wait indefinitely for detection to finish
			while(pIsDetectionInProgress)
			{
				Thread.Sleep(100);
				Thread.Sleep(0);
			}
#endif

#if Smartphone && !Framework20
			try
			{
				DiscoveryWaitHandle.Close();
			}
			catch
			{
			}
#endif
#if Framework20 && !PocketPC
            if (!DetectionWaitHandle.SafeWaitHandle.IsClosed)
#else
                if (DetectionWaitHandle.Handle != new IntPtr(-1))
#endif
            {
                try
                {
                    DetectionWaitHandle.Close();
                }
                catch
                {
                }
            }

#if Framework20 && !PocketPC
            if (!DetectionWaitHandle.SafeWaitHandle.IsClosed)
#else
                if (DetectionWaitHandle.Handle != new IntPtr(-1))
#endif
            {
                try
                {
                    DeviceDetectedWaitHandle.Close();
                }
                catch
                {
                }
            }
        }

        #endregion

        #region Events

        /// <summary>
		/// Occurs when an attempt is about to be made to recognize a device as a GPS
		/// device.
		/// </summary>
		/// <remarks>
		/// This event is called immediately before an interpreter is tested with data from a
		/// device. If the attempt succeeds, the <strong>DeviceDetected</strong> event is
		/// raised.
		/// </remarks>
		/// <example>
		///     This example uses the <strong>DeviceDetectionAttempted</strong> event to monitor
		///     the progress of device detection. 
		///     <code lang="VB" title="[New Example]">
		/// Imports GeoFramework.IO
		///  
		/// Sub Main()
		///    AddHandler Devices.DeviceDetectionAttempted, AddressOf OnDetectionAttempted
		///    
		///    ' Now start device detection
		///    Devices.Detect()
		/// End Sub
		///  
		/// Private Sub OnDetectionAttempted(ByVal sender As Object, ByVal e As DeviceEventArgs)
		///    ' Output the device being attempted
		///    Debug.WriteLine("Detecting protocol for " &amp; e.Device.ToString())
		/// End Sub
		///     </code>
		/// 	<code lang="CS" title="[New Example]">
		/// using GeoFramework.IO;
		///  
		/// void Main()
		/// {
		///    Devices.DeviceDetectionAttempted += new DeviceEventHandler(OnDetectionAttempted);
		///    
		///    // Now start device detection
		///    Devices.Detect();
		/// }
		///  
		/// private void OnDetectionAttempted(object sender, DeviceEventArgs e)
		/// {
		///    // Output the device being attempted
		///    Console.WriteLine("Detecting protocol for " &amp; e.Device.ToString());
		/// }
		///     </code>
		/// </example>
		public static event DeviceEventHandler DeviceDetectionAttempted;
		/// <summary>Occurs when device detection is about to scan for devices.</summary>
		/// <remarks>
		/// This event is raised immediately after a call to the <strong>Detect</strong>
		/// method is made. After this event is raised, detection commences for all known devices.
		/// When detection has completed successfully, the
		/// <strong>DeviceDetectionCompleted</strong> event is raised. If detection is cancelled
		/// via the <strong>CancelDetection</strong> method, the
		/// <strong>DeviceDetectionCanceled</strong> event is raised.
		/// </remarks>
		/// <example>
		///     This example uses the <strong>DeviceDetectionStarted</strong> event to indicate
		///     when device detection has begun. 
		///     <code lang="VB" title="[New Example]">
		/// Imports GeoFramework.IO
		///  
		/// Sub Main()
		///    AddHandler Devices.DeviceDetectionStarted, AddressOf OnDetectionStarted
		///    
		///    ' Now start device detection
		///    Devices.Detect()
		/// End Sub
		///  
		/// Private Sub OnDetectionStarted(ByVal sender As Object, ByVal e As EventArgs)
		///    ' Output the device being attempted
		///    Debug.WriteLine("Starting detection...")
		/// End Sub
		///     </code>
		/// 	<code lang="CS" title="[New Example]">
		/// using GeoFramework.IO;
		///  
		/// void Main()
		/// {
		///    Devices.DeviceDetectionStarted += new EventHandler(OnDetectionAttempted);
		///    
		///    // Now start device detection
		///    Devices.Detect();
		/// }
		///  
		/// private void OnDetectionStarted(object sender, EventArgs e)
		/// {
		///    Console.WriteLine("Starting detection...");
		/// }
		///     </code>
		/// </example>
		public static event EventHandler DeviceDetectionStarted;

		
		/// <summary>Occurs when device detection has been aborted.</summary>
		public static event DeviceEventHandler DeviceDetectionCanceled;
		/// <summary>Occurs when device detection has completed successfully.</summary>
		public static event EventHandler DeviceDetectionCompleted;
		/// <summary>Occurs when an unhandled exception is thrown during device detection.</summary>
		public static event ExceptionEventHandler DeviceDetectionExceptionOccurred;
		/// <summary>Occurs when a device has been identified as a GPS device.</summary>
		public static event DeviceEventHandler DeviceDetected;
		/// <summary>Occurs when a GPS device has stopped responding.</summary>
		public static event DeviceEventHandler DeviceFailure;
		/// <summary>Occurs when a GPS device is discovered on a serial port.</summary>
		public static event DeviceEventHandler SerialDeviceDetected;
		/// <summary>Occurs when a GPS device is discovered on a serial port.</summary>
		public static event DeviceEventHandler BluetoothDeviceDetected;
		/// <summary>Occurs when potential GPS devices need to be registered with GPS.NET before detection begins.</summary>
		/// <remarks>Before Automatic GPS Device detection begins, GPS.NET builds a list of serial, USB, Bluetooth and other
		/// device types.  This event allows for custom devices to be registered for scanning.  For example, this event is
		/// used by the Garmin® plug-in to register Garmin USB devices that would not otherwsie get scanned.</remarks>
		public static event EventHandler DeviceEnumeration;
#if !PocketPC
        /// <summary>Occurs when a GPS device is discovered on a USB port.</summary>
        public static event DeviceEventHandler UsbDeviceDetected;
#endif
		/// <summary>Occurs when the current direction of travel has changed.</summary>
		public static event AzimuthEventHandler CurrentBearingChanged;
		/// <summary>Occurs when the current distance above sea level has changed.</summary>
		public static event DistanceEventHandler CurrentAltitudeChanged;
		/// <summary>Occurs when the current rate of travel has changed.</summary>
		public static event SpeedEventHandler CurrentSpeedChanged;
		/// <summary>Occurs when GPS satellite information has changed.</summary>
		public static event SatelliteCollectionEventHandler CurrentSatellitesChanged;
		/// <summary>Occurs when a GPS fix becomes active.</summary>
		public static event EventHandler CurrentFixObtained;
		/// <summary>Occurs when an active GPS fix is no longer active.</summary>
		public static event EventHandler CurrentFixLost;
		/// <summary>Occurs when the current satellite-derived time has changed.</summary>
		public static event DateTimeEventHandler CurrentUtcDateTimeChanged;
		/// <summary>Occurs when the current location on Earth has changed.</summary>
		public static event PositionEventHandler CurrentPositionChanged;
		/// <summary>Occurs when the overall amount of precision has changed.</summary>
		public static event DilutionOfPrecisionEventHandler CurrentMeanDilutionOfPrecisionChanged;
		/// <summary>Occurs when the amount of positional precision has changed.</summary>
		public static event DilutionOfPrecisionEventHandler CurrentHorizontalDilutionOfPrecisionChanged;
		/// <summary>Occurs when the current altitude precision has changed.</summary>
		public static event DilutionOfPrecisionEventHandler CurrentVerticalDilutionOfPrecisionChanged;

        #endregion

        #region Public Members

#if PocketPC && !Smartphone
        /// <summary>
		/// Returns the intermediate GPS driver if it is current supported by the system.
		/// </summary>
		/// <remarks>Beginning with the Windows Mobile 5.0 operating system, Microsoft introduced the
		/// GPS Intermediate Driver.  This layer allows GPS applications to share a single GPS device.
		/// This property returns the intermediate driver information.</remarks>
		public static SharedGpsDevice SharedGpsDevice
		{
			get
			{
				return pSharedGpsDevice;
			}
		}

        /// <summary>
        /// Returns a list of serial ports which are avoided during device detection.
        /// </summary>
        /// <remarks>In some cases, the device detection process might interfere with other devices on the system.  
        /// Devices such as bar code scanners or custom hardware might react unfavorably when GPS.NET connects to them.
        /// To prevent GPS.NET from testing a serial port, the port (a CommunicationPort or Integer value) is added to
        /// this array.  A port can also be avoided using the AvoidSerialPort method.</remarks>
        public static ArrayList AvoidedSerialPorts
        {
            get
            {
                return pAvoidedSerialPorts;
            }
        }

		/// <summary>
		/// Controls whether the Intermediate GPS Device is automatically used when supported.
		/// </summary>
		public static bool IsSharedGpsDeviceAutomaticallyConfigured
		{
			get
			{
				return pIsSharedGpsDeviceAutomaticallyConfigured;
			}
			set
			{
				pIsSharedGpsDeviceAutomaticallyConfigured = value;
			}
		}

		/// <summary>
		/// Controls whether GPS.NET is allowed to use the GPS Intermediate Driver if it is available.
		/// </summary>
		public static bool AllowSharedGpsDeviceConnections
		{
			get
			{
				return pAllowSharedGpsDeviceConnections;
			}
			set
			{
				pAllowSharedGpsDeviceConnections = value;
			}
		}
#endif

		/// <summary>
		/// Controls the maximum COM: serial port to test when searching for GPS devices.
		/// </summary>
		/// <remarks>
		/// <para>During Automatic GPS Device Detection, GPS.NET will perform a 
		/// scan of the local system in an attempt to discover GPS devices.  Since not all GPS
		/// devices properly report their existance in the registry, and cannot be quickly located,
		/// a scan of all serial ports is typically necessary to find the device.  This property
		/// controls the maximum serial port which is scanned.  For example, a value of
		/// twenty indicates that GPS.NET will test ports COM0: through COM20:.  GPS devices
		/// (especially USB devices) are sometimes found with COM: port numbers as high as COM40:.</para>
		/// <para>This property defaults to twenty and should be changed before device detection begins.
		/// This property is ignored if the <strong>IsExhaustiveDetectionEnabled</strong> property
		/// is False, since only devices found in the registry are scanned.</para></remarks>
		public static int MaximumSerialPortToScan
		{
			get
			{
				return pMaximumSerialPortToScan;
			}
			set
			{
				pMaximumSerialPortToScan = value;
			}
        }

        /// <summary>Returns the current satellite-derived time.</summary>
#if !PocketPC
        [TypeConverter(typeof(ExpandableObjectConverter))]
#endif
		public static DateTime CurrentUtcDateTime
		{
			get
			{
				return pCurrentUtcDateTime;
			}
		}

		/// <summary>Sets the current satellite-derived time.</summary>
		public static void SetCurrentUtcDateTime(object sender, DateTime dateTime)
		{
			if (pCurrentUtcDateTime.Equals(dateTime)) return;
			pCurrentUtcDateTime = dateTime;

			#region Update the System Clock

			if (pCurrentlyFixed && pIsClockSynchronizationEnabled)
			{
				// It's safe to update the system clock
				// Declare a new SystemTime structure
				SYSTEMTIME NewTime = new SYSTEMTIME();
				// Convert the time to local time zone
				DateTime LocalTime = pCurrentUtcDateTime.ToLocalTime();
				// Move the values from the specified date & time to the structure
				NewTime.Year = (short)LocalTime.Year;
				NewTime.Month = (short)LocalTime.Month;
				NewTime.Day = (short)LocalTime.Day;
				NewTime.DayOfWeek = (short)LocalTime.DayOfWeek;
				NewTime.Hour = (short)LocalTime.Hour;
				NewTime.Minute = (short)LocalTime.Minute;
				NewTime.Second = (short)LocalTime.Second;
				NewTime.Milliseconds = (short)LocalTime.Millisecond;
				// Finally, set the system clock			
				NativeMethods.SetLocalTime(ref NewTime);
			}

			#endregion

			if (CurrentUtcDateTimeChanged != null)
#if PocketPC && !Framework20
				CurrentUtcDateTimeChanged(sender, new DateTimeEventArgs(dateTime));
#else
                DynamicEvent.BeginInvoke(CurrentUtcDateTimeChanged, sender, new DateTimeEventArgs(pCurrentUtcDateTime));
#endif
		}

		/// <summary>Returns the current distance above sea level.</summary>
#if !PocketPC
        [TypeConverter(typeof(ExpandableObjectConverter))]
#endif
		public static Distance CurrentAltitude
		{
			get
			{
				return pCurrentAltitude;
			}
		}

		/// <summary>Sets the current altitude above sea level.</summary>
		public static void SetCurrentAltitude(object sender, Distance altitude)
		{
			if (pCurrentAltitude.Equals(altitude)) return;
			pCurrentAltitude = altitude;
			if (CurrentAltitudeChanged != null)
#if PocketPC && !Framework20
				CurrentAltitudeChanged(sender, new DistanceEventArgs(pCurrentAltitude));
#else
                DynamicEvent.BeginInvoke(CurrentAltitudeChanged, sender, new DistanceEventArgs(pCurrentAltitude));
#endif
		}

		/// <summary>Returns the current overall precision.</summary>
#if !PocketPC
        [TypeConverter(typeof(ExpandableObjectConverter))]
#endif
		public static DilutionOfPrecision CurrentMeanDilutionOfPrecision
		{
			get
			{
				return pCurrentMeanDilutionOfPrecision;
			}
		}

		/// <summary>Sets the current overall amount of precision.</summary>
		public static void SetCurrentMeanDilutionOfPrecision(object sender, DilutionOfPrecision meanDilutionOfPrecision)
		{
			if (pCurrentMeanDilutionOfPrecision.Equals(meanDilutionOfPrecision)) return;
			pCurrentMeanDilutionOfPrecision = meanDilutionOfPrecision;
			if (CurrentMeanDilutionOfPrecisionChanged != null)
#if PocketPC && !Framework20
				CurrentMeanDilutionOfPrecisionChanged(sender, new DilutionOfPrecisionEventArgs(pCurrentMeanDilutionOfPrecision));
#else
                DynamicEvent.BeginInvoke(CurrentMeanDilutionOfPrecisionChanged, sender, new DilutionOfPrecisionEventArgs(pCurrentMeanDilutionOfPrecision));
#endif
		}

		/// <summary>Returns the current altitude precision.</summary>
#if !PocketPC
        [TypeConverter(typeof(ExpandableObjectConverter))]
#endif
		public static DilutionOfPrecision CurrentVerticalDilutionOfPrecision
		{
			get
			{
				return pCurrentVerticalDilutionOfPrecision;
			}
		}

		/// <summary>Sets the current amount of altitude precision.</summary>
		public static void SetCurrentVerticalDilutionOfPrecision(object sender, DilutionOfPrecision verticalDilutionOfPrecision)
		{
			if (pCurrentVerticalDilutionOfPrecision.Equals(verticalDilutionOfPrecision)) return;
			pCurrentVerticalDilutionOfPrecision = verticalDilutionOfPrecision;
			if (CurrentVerticalDilutionOfPrecisionChanged != null)
#if PocketPC && !Framework20
				CurrentVerticalDilutionOfPrecisionChanged(sender, new DilutionOfPrecisionEventArgs(pCurrentVerticalDilutionOfPrecision));
#else
                DynamicEvent.BeginInvoke(CurrentVerticalDilutionOfPrecisionChanged, sender, new DilutionOfPrecisionEventArgs(pCurrentVerticalDilutionOfPrecision));
#endif
		}

		/// <summary>Returns the current position precision.</summary>
#if !PocketPC
        [TypeConverter(typeof(ExpandableObjectConverter))]
#endif
		public static DilutionOfPrecision CurrentHorizontalDilutionOfPrecision
		{
			get
			{
				return pCurrentHorizontalDilutionOfPrecision;
			}
		}

		/// <summary>Sets the current position precision.</summary>
		public static void SetCurrentHorizontalDilutionOfPrecision(object sender, DilutionOfPrecision horizontalDilutionOfPrecision)
		{
			if (pCurrentHorizontalDilutionOfPrecision.Equals(horizontalDilutionOfPrecision)) return;
			pCurrentHorizontalDilutionOfPrecision = horizontalDilutionOfPrecision;
			if (CurrentHorizontalDilutionOfPrecisionChanged != null)
#if PocketPC && !Framework20
				CurrentHorizontalDilutionOfPrecisionChanged(sender, new DilutionOfPrecisionEventArgs(pCurrentHorizontalDilutionOfPrecision));
#else
                DynamicEvent.BeginInvoke(CurrentHorizontalDilutionOfPrecisionChanged, sender, new DilutionOfPrecisionEventArgs(pCurrentHorizontalDilutionOfPrecision));
#endif
		}

		/// <summary>
		/// Controls whether connections are allowed to Bluetooth devices during automatic GPS device detection.
		/// </summary>
		public static bool AllowBluetoothConnections
		{
			get
			{
				return pAllowBluetoothConnections;
			}
			set
			{
				pAllowBluetoothConnections = value;
			}
		}

		/// <summary>
		/// Controls whether connections are allowed to serial devices during automatic GPS device detection.
		/// </summary>
		public static bool AllowSerialConnections
		{
			get
			{
				return pAllowSerialConnections;
			}
			set
			{
				pAllowSerialConnections = value;
			}
		}


        /// <summary>
        /// Indicates whether the device is able to report how many bytes are available to read.
        /// </summary>
        /// <value>A Boolean.  Defaults to True.</value>
        /// <remarks><para>This property is provided to help developers work around manufacturing flaws discovered in GPS and mobile devices.
        /// Some devices, such as the HTC TyTN II and AT&T Tilt, when asked how many bytes are waiting to be read, will always return zero even
        /// if data is actually available.  This flaw causes GPS.NET to avoid reading data even though data actually IS available.  This property,
        /// when set to <strong>False</strong>, will cause GPS.NET to skip checking the port for available bytes, which works around 
        /// the manufacturing flaw.  By default this property is <strong>True</strong> since most devices to correctly report the
        /// number of bytes waiting to be read.</para>
        /// <para>If you experience odd behavior such as a "burst" of NMEA data followed by a timeout, setting this property to <strong>False</strong>
        /// could resolve the issue.  Setting this property to <strong>False</strong> also has a slight performance boost since it bypasses several
        /// API calls, but it also slightly increases the risk of a hang occurring when an attempt to read is made, but no data is available.  Since
        /// a vast majority of GPS devices are (and should be) constantly transmitting data, the risk is very small.</para></remarks>
        public static bool IsSerialDeviceQueriedBeforeReading
        {
            get
            {
                return _IsSerialDeviceQueriedBeforeReading;
            }
            set
            {
                _IsSerialDeviceQueriedBeforeReading = value;
            }
        }

		/// <summary>
		/// Controls whether the last-used GPS device is recorded in order to speed up device detection.
		/// </summary>
		public static bool IsDeviceCacheEnabled
		{
			get
			{                
				return pIsDeviceCacheEnabled;
			}
			set
			{
				pIsDeviceCacheEnabled = value;
			}
		}

		/// <summary>Returns the current rate of travel.</summary>
#if !PocketPC
        [TypeConverter(typeof(ExpandableObjectConverter))]
#endif
		public static Speed CurrentSpeed
		{
			get
			{
				return pCurrentSpeed;
			}
		}

		/// <summary>Sets the current rate of travel.</summary>
		public static void SetCurrentSpeed(object sender, Speed speed)
		{
			if (pCurrentSpeed.Equals(speed)) return;
			pCurrentSpeed = speed;
			if (CurrentSpeedChanged != null)
#if PocketPC && !Framework20
				CurrentSpeedChanged(sender, new SpeedEventArgs(pCurrentSpeed));
#else
                DynamicEvent.BeginInvoke(CurrentSpeedChanged, sender, new SpeedEventArgs(pCurrentSpeed));
#endif
		}

		/// <summary>Returns the current collection of GPS satellites.</summary>
#if !PocketPC
        [TypeConverter(typeof(ExpandableObjectConverter))]
#endif
		public static SatelliteCollection CurrentSatellites
		{
			get
			{
				return pCurrentSatellites;
			}
		}

		/// <summary>Sets the current collection of GPS satellite information.</summary>
		public static void SetCurrentSatellites(object sender, SatelliteCollection satellites)
		{
#if !PocketPC || Framework20
            GeoFramework.Threading.ThreadPool.Queue(new WaitCallback(BeginSetCurrentSatellites), new object[] { sender, satellites });
#else
			BeginSetCurrentSatellites(new object[] { sender, satellites });
#endif
		}

		private static void BeginSetCurrentSatellites(object state)
		{
			object[] values = state as object[];
			object sender = values[0];
			SatelliteCollection satellites = (SatelliteCollection)values[1];

            lock (satellites.SyncRoot)
            {
                lock (pCurrentSatellites)
                {
                    if (pCurrentSatellites.MergeWith(satellites))
                    {
                        // And notify if there's a change
                        if (CurrentSatellitesChanged != null)
#if PocketPC && !Framework20
						System.Threading.ThreadPool.QueueUserWorkItem(new WaitCallback(BeginCurrentSatellitesChanged), new SatelliteCollectionEventArgs(pCurrentSatellites));
#else
                            DynamicEvent.BeginInvoke(CurrentSatellitesChanged, sender, new SatelliteCollectionEventArgs(pCurrentSatellites));
#endif
                    }
                }
            }
		}

#if PocketPC && !Framework20
		private static void BeginCurrentSatellitesChanged(object state)
		{
			try
			{
				if(CurrentSatellitesChanged != null)
					CurrentSatellitesChanged(null, (SatelliteCollectionEventArgs)state);
			}
			catch
			{
			}
		}
#endif

		/// <summary>Returns the current direction of travel.</summary>
#if !PocketPC
        [TypeConverter(typeof(ExpandableObjectConverter))]
#endif
		public static Azimuth CurrentBearing
		{
			get
			{
				return pCurrentBearing;
			}
		}

		/// <summary>Sets the current direction of travel.</summary>
		//[CLSCompliant(false)]
		public static void SetCurrentBearing(object sender, Azimuth bearing)
		{
			if (pCurrentBearing.Equals(bearing)) return;
			pCurrentBearing = bearing;
			if (CurrentBearingChanged != null)
#if PocketPC && !Framework20
				CurrentBearingChanged(sender, new AzimuthEventArgs(pCurrentBearing));
#else
                DynamicEvent.BeginInvoke(CurrentBearingChanged, sender, new AzimuthEventArgs(pCurrentBearing));
#endif
		}

		/// <summary>Returns whether the GPS device has a fix.</summary>
		public static bool CurrentlyFixed
		{
			get
			{
				return pCurrentlyFixed;
			}
		}


		/// <summary>Sets whether or not a GPS device has a fix.</summary>
		public static void SetCurrentlyFixed(object sender, bool isFixed)
		{
			if (pCurrentlyFixed == isFixed)
				return;

			pCurrentlyFixed = isFixed;

			if (pCurrentlyFixed)
			{
				if (CurrentFixObtained != null)
#if PocketPC && !Framework20
					CurrentFixObtained(sender, EventArgs.Empty);
#else
                    DynamicEvent.BeginInvoke(CurrentFixObtained, sender, EventArgs.Empty);
#endif
			}
			else
			{
				if (CurrentFixLost != null)
#if PocketPC && !Framework20
					CurrentFixLost(sender, EventArgs.Empty);
#else
                    DynamicEvent.BeginInvoke(CurrentFixLost, sender, EventArgs.Empty);
#endif
			}
		}

		/// <summary>Returns the current location on Earth.</summary>
#if !PocketPC
        [TypeConverter(typeof(ExpandableObjectConverter))]
#endif
		public static Position CurrentPosition
		{
			get
			{
				return pCurrentPosition;
			}
		}

		/// <summary>Sets the current location on Earth.</summary>
		public static void SetCurrentPosition(object sender, Position position)
		{
			if (pCurrentPosition.Equals(position)) return;
			pCurrentPosition = position;
			if (CurrentPositionChanged != null)
#if PocketPC && !Framework20
				CurrentPositionChanged(sender, new PositionEventArgs(pCurrentPosition, CurrentUtcDateTime));
#else
                DynamicEvent.BeginInvoke(CurrentPositionChanged, sender, new PositionEventArgs(pCurrentPosition, CurrentUtcDateTime));
#endif
		}

		/// <summary>
		/// Notifies GPS.NET that a valid GPS device has been located and is ready to use.
		/// </summary>
		/// <param name="device">A <strong>Device</strong> object describing the GPS device.</param>
		/// <param name="protocol">A <strong>String</strong> describing the name of the GPS data protocol in use on the device.</param>
		/// <remarks><para>In some situations, a GPS device may be guaranteed to be found on a specific, unchanging port.  For example, mobile devices
		/// such as the Hewlett-Packard iPAQ® 6510 have a GPS device on COM7 at 57,600 baud... which will never change.  This method provides
		/// a way to tell GPS.NET about a GPS device.  The specific protocol in use by the device helps GPS.NET make the appropriate
		/// data connections when GPS data is requested.</para>
		/// <para>The <strong>Protocol</strong> parameter is a <strong>String</strong> describing the name of the protocol.  This is
		/// best found by examining the static (Shared in Visual Basic) <strong>Protocol</strong> property of an interpreter class, such as the 
		/// <strong>NmeaInterpreter.</strong></para></remarks>
		/// <example>This example registers a device at COM7 at 57,600 baud using the NMEA-0183 GPS data protocol.
		/// <code lang=cs"></code>
		/// using GeoFramework.IO;
		/// using GeoFramework.IO.Serial;
		/// 
		/// void RegisterIntegratedDevice() 
		/// {
		///   // Create a new serial device on COM7 at 57,600 baud
		///   SerialDevice IntegratedDevice = new SerialDevice(CommunicationPort.Com7, BaudRate.Baud57600);
		/// 
		///   // Notify GPS.NET that this is a valid GPS device
		///   Devices.RegisterGpsDevice(IntegratedDevice);
		/// 
		///   // Force GPS.NET to only use this device, effectively skipping
		///   // automatic GPS device detection.
		///   Devices.IsOnlyFirstDeviceDetected = true;
		/// }
		/// </example>
		public static void RegisterGpsDevice(Device device, string protocol)
		{
			// Set the protocol of the device
			device.SetProtocol(protocol);

			// And register it on the system
			RegisterGpsDevice(device);
		}

		/// <summary>
		/// Notifies GPS.NET that a valid GPS device has been located and is ready to use.
		/// </summary>
		/// <param name="device">A <strong>Device</strong> object describing the GPS device.</param>
		/// <remarks><para>During automatic GPS device detection, GPS.NET tests each known serial and
		/// Bluetooth® device with each registered GPS data interpreter.  If an interpreter successfully
		/// identifies data from the device, this method is called to record the device.  Once a device
		/// has been registered, GPS.NET can automatically open connections to the device.</para></remarks>
		public static void RegisterGpsDevice(Device device)
		{
			try
			{
				// If the protocol is missing, complain
				if (device.Protocol == null || device.Protocol.Length == 0)
					throw new ArgumentNullException("device.Protocol", "In order for a device to be registered as a GPS device, its GPS data protocol name must be known.  The device specified has no protocol information.  Call the RegisterGpsDevice method with an extra \"protocol\" parameter to solve this issue.");

				// Reset the failure count for the device
				//device.FailureCount = 0;
				device.SetIsGpsDevice(true);

				// Is it already in the list?
				if (pGpsDevices.Contains(device))
				{
					// And flag that a device is detected
					DeviceDetectedWaitHandle.Set();
					return;
				}

				// What kind of device is it?
				SerialDevice DeviceAsSerial = device as SerialDevice;
				BluetoothDevice DeviceAsBluetooth = device as BluetoothDevice;
				VirtualDevice DeviceAsVirtualDevice = device as VirtualDevice;
#if !PocketPC
                UsbDevice DeviceAsUsb = device as UsbDevice;
#endif

#if PocketPC
				SharedGpsDevice DeviceAsSharedGps = device as SharedGpsDevice;

                // If we have a serial device, and the GPSID is supported, we may be able to
                // configure it or even use it.
                if(DeviceAsSerial != null && SharedGpsDevice.IsSupported)
                {
                    // Are we allowed to configure the GPSID?
                    if(pIsSharedGpsDeviceAutomaticallyConfigured)
                    {
                        // Enable a shared port if none exists
                        if (pSharedGpsDevice.SharedPortName == null)
                            pSharedGpsDevice.SharedPortName = "GPD1:";

                        // Set the device port is none exists
                        if(pSharedGpsDevice.SourceDevice == null)
                            pSharedGpsDevice.SourceDevice = DeviceAsSerial;
                    }

                    // Now.  Is the GPSID actually enabled?
                    if(pSharedGpsDevice.IsEnabled)
                    {
                        // Transfer the open serial stream to the shared GPS device
                        if (DeviceAsSerial.IsImmediateNeedStreamAvailable)
                        {
                            DeviceAsSerial.IsImmediateNeedStreamAvailable = false;
                            pSharedGpsDevice.SetImmediateNeedStream(DeviceAsSerial.ImmediateNeedStream);
                            DeviceAsSerial.SetImmediateNeedStream(null);
                            pSharedGpsDevice.IsImmediateNeedStreamAvailable = true;
                        }

                        // Finally, connect to the intermediate driver and tell it
                        // to refresh its settings.  Get with the program, buddy
                        pSharedGpsDevice.Refresh();

                        // Finally, register the device with GPS.NET
                        RegisterGpsDevice(pSharedGpsDevice, DeviceAsSerial.Protocol);

                        // Don't add the serial device as a GPS device. We're using
                        // the shared GPS device instead
                        return;
                    }
                }

#endif

                // Lock the collection before modifying it
				lock (pGpsDevices.SyncRoot)
				{
					// No.  Go ahead and add it
					pGpsDevices.Add(device);
				}
				Thread.Sleep(0);

				// Mark it as the first responding device
				if (pGpsDevices.Count == 1)
				{
                    // If only one GPS device is required, stop detection now in the background
                    if (pIsOnlyFirstDeviceDetected)
                    {
                        ThreadPool.QueueUserWorkItem(new WaitCallback(CancelDetection));
                    }

					// Update the registry to use this device!
					RegistryKey BestDeviceKey = null;
					try
					{
						BestDeviceKey = Registry.CurrentUser.OpenSubKey(@"Software\GeoFrameworks\GPS.NET\2.0\", true);
						if (BestDeviceKey == null)
							BestDeviceKey = Registry.CurrentUser.CreateSubKey(@"Software\GeoFrameworks\GPS.NET\2.0\");


						if (DeviceAsSerial != null)
						{
							BestDeviceKey.SetValue("Last Used Device", @"Serial Devices\" + DeviceAsSerial.ToString());
						}
						else if (DeviceAsBluetooth != null)
						{
							BestDeviceKey.SetValue("Last Used Device", @"Bluetooth Devices\" + DeviceAsBluetooth.ToString());
						}
#if PocketPC
						else if (DeviceAsSharedGps != null)
						{
							BestDeviceKey.SetValue("Last Used Device", @"Shared GPS Device");
						}
#endif
#if !PocketPC
                        else if (DeviceAsUsb != null)
                        {
                            BestDeviceKey.SetValue("Last Used Device", @"USB Devices\" + DeviceAsUsb.ToString());
                        }
#endif
					}
					catch
					{
						// Ignore errors during device hints
					}
					finally
					{
						if (BestDeviceKey != null)
							BestDeviceKey.Close();
					}
				}

				// And flag that a device is detected
				DeviceDetectedWaitHandle.Set();

				// Notify of the new device
				OnDeviceDetected(device);

				// Is it a serial device?
				if (DeviceAsSerial != null)
				{
					#region Serial GPS Device Hints

					if (!pSerialGpsDevices.Contains(DeviceAsSerial))
					{
						// No.  Lock it and add
						lock (pSerialGpsDevices.SyncRoot)
						{
							pSerialGpsDevices.Add(DeviceAsSerial);
						}

						// Notify of the new item
						OnSerialDeviceDetected(DeviceAsSerial);

						// On PocketPC's, add a registry key pointing to this device.
						// This will speed up device detection later on.
						RegistryKey DeviceHints = null;
						try
						{
							DeviceHints = Registry.CurrentUser.OpenSubKey(@"Software\GeoFrameworks\GPS.NET\2.0\Serial Devices\" + DeviceAsSerial.ToString(), true);
							if (DeviceHints == null)
								DeviceHints = Registry.CurrentUser.CreateSubKey(@"Software\GeoFrameworks\GPS.NET\2.0\Serial Devices\" + DeviceAsSerial.ToString());
							// Set the port, baud rate, and more
							DeviceHints.SetValue("Port Name", DeviceAsSerial.PortName);
							DeviceHints.SetValue("Baud Rate", (int)DeviceAsSerial.BaudRate);
							DeviceHints.SetValue("Data Bits", (int)DeviceAsSerial.DataBits);
							DeviceHints.SetValue("Stop Bits", (int)DeviceAsSerial.StopBits);
							DeviceHints.SetValue("Protocol", DeviceAsSerial.Protocol);
						}
						catch
						{
							// Ignore errors during device hints
						}
						finally
						{
							if (DeviceHints != null)
								DeviceHints.Close();
						}
					}

					#endregion
				}
				else if (DeviceAsBluetooth != null)
				{
					#region Bluetooth GPS Device Hints

					if (!pBluetoothGpsDevices.Contains(DeviceAsBluetooth))
					{
						// No.  Lock it and add
						lock (pBluetoothGpsDevices.SyncRoot)
						{
							pBluetoothGpsDevices.Add(DeviceAsBluetooth);
						}

						// Notify of the new item
						OnBluetoothDeviceDetected(DeviceAsBluetooth);

						// On PocketPC's, add a registry key pointing to this device.
						// This will speed up device detection later on.
						RegistryKey DeviceHints = null;
						try
						{
							DeviceHints = Registry.CurrentUser.OpenSubKey(@"Software\GeoFrameworks\GPS.NET\2.0\Bluetooth Devices\" + DeviceAsBluetooth.ToString(), true);
							if (DeviceHints == null)
								DeviceHints = Registry.CurrentUser.CreateSubKey(@"Software\GeoFrameworks\GPS.NET\2.0\Bluetooth Devices\" + DeviceAsBluetooth.ToString());
							DeviceHints.SetValue("Name", DeviceAsBluetooth.DeviceName);
							DeviceHints.SetValue("Address", DeviceAsBluetooth.Address.ToString());
							DeviceHints.SetValue("Protocol", DeviceAsBluetooth.Protocol);
						}
						catch
						{
							// Ignore errors during hints
						}
						finally
						{
							if (DeviceHints != null)
								DeviceHints.Close();
						}
					}

					#endregion
				}
				else if (DeviceAsVirtualDevice != null)
				{
					#region Virtual GPS Device Hints

					if (!pVirtualGpsDevices.Contains(DeviceAsVirtualDevice))
					{
						lock (pVirtualGpsDevices.SyncRoot)
						{
							pVirtualGpsDevices.Add(DeviceAsVirtualDevice);
						}
					}

					#endregion

				}
#if !PocketPC
                else if (DeviceAsUsb != null)
                {
					#region USB Devices

                    if (!pUsbGpsDevices.Contains(DeviceAsUsb))
                    {
                        // No.  Lock it and add
                        lock (pUsbGpsDevices.SyncRoot)
                        {
                            pUsbGpsDevices.Add(DeviceAsUsb);
                        }

                        // Notify of the new item
                        OnUsbDeviceDetected(DeviceAsUsb);

                        // On PocketPC's, add a registry key pointing to this device.
                        // This will speed up device detection later on.
                        RegistryKey DeviceHints = null;
                        try
                        {
                            DeviceHints = Registry.CurrentUser.OpenSubKey(@"Software\GeoFrameworks\GPS.NET\2.0\USB Devices\" + DeviceAsUsb.ToString(), true);
                            if (DeviceHints == null)
                                DeviceHints = Registry.CurrentUser.CreateSubKey(@"Software\GeoFrameworks\GPS.NET\2.0\USB Devices\" + DeviceAsUsb.ToString());
                            DeviceHints.SetValue("Name", DeviceAsUsb.DeviceName);
                            DeviceHints.SetValue("Path", DeviceAsUsb.Path);
                            DeviceHints.SetValue("Protocol", DeviceAsUsb.Protocol);
                        }
                        catch
                        {
                            // Ignore errors during hints
                        }
                        finally
                        {
                            if (DeviceHints != null)
                                DeviceHints.Close();
                        }
                    }

					#endregion
                }
#endif
#if PocketPC
				else if (DeviceAsSharedGps != null)
				{
					#region Shared GPS Settings

					// On PocketPC's, add a registry key pointing to this device.
					// This will speed up device detection later on.
					RegistryKey DeviceHints = null;
					try
					{
						DeviceHints = Registry.CurrentUser.OpenSubKey(@"Software\GeoFrameworks\GPS.NET\2.0\Shared GPS Device", true);
						if (DeviceHints == null)
							DeviceHints = Registry.CurrentUser.CreateSubKey(@"Software\GeoFrameworks\GPS.NET\2.0\Shared GPS Device");
						// Set the port, baud rate, and more
						DeviceHints.SetValue("Detected GPS Device", DeviceAsSharedGps.SourceDevice.ToString());
					}
					catch
					{
						// Ignore errors during device hints
					}
					finally
					{
						if (DeviceHints != null)
							DeviceHints.Close();
					}

					#endregion
				}
#endif
			}
			catch
			{
				throw;
			}
			finally
			{


				// Whee!
				//GC.Collect();
				//GC.WaitForPendingFinalizers();

			}
		}

		private static void OnDeviceDetected(Device device)
		{
			if (DeviceDetected != null)
#if PocketPC && !Framework20
				System.Threading.ThreadPool.QueueUserWorkItem(new WaitCallback(OnDeviceDetectedThreadProc), device);
#else
                DynamicEvent.BeginInvoke(DeviceDetected, device, new DeviceEventArgs(device));
#endif
		}

#if PocketPC && !Framework20
		private static void OnDeviceDetectedThreadProc(object state)
		{
			try
			{
				DeviceDetected(state, new DeviceEventArgs((Device)state));
			}
			catch
			{
			}
		}
#endif

		private static void OnDeviceFailure(Device device)
		{
			if (DeviceFailure != null)
#if PocketPC && !Framework20
				System.Threading.ThreadPool.QueueUserWorkItem(new WaitCallback(OnDeviceFailureThreadProc), device);
#else
                DynamicEvent.BeginInvoke(DeviceFailure, device, new DeviceEventArgs(device));
#endif
		}

#if PocketPC && !Framework20
		private static void OnDeviceFailureThreadProc(object state)
		{
			try
			{
				DeviceFailure(state, new DeviceEventArgs((Device)state));
			}
			catch
			{
			}
		}
#endif

		private static void OnSerialDeviceDetected(SerialDevice device)
		{
			if (SerialDeviceDetected != null)
#if PocketPC && !Framework20
				System.Threading.ThreadPool.QueueUserWorkItem(new WaitCallback(OnSerialDeviceDetectedThreadProc), device);
#else
                DynamicEvent.BeginInvoke(SerialDeviceDetected, device, new DeviceEventArgs(device));
#endif
		}

#if PocketPC && !Framework20
		private static void OnSerialDeviceDetectedThreadProc(object state)
		{
			try
			{
				SerialDeviceDetected(state, new DeviceEventArgs((Device)state));
			}
			catch
			{
			}
		}
#endif

		private static void OnBluetoothDeviceDetected(BluetoothDevice device)
		{
			if (BluetoothDeviceDetected != null)
#if PocketPC && !Framework20
				System.Threading.ThreadPool.QueueUserWorkItem(new WaitCallback(OnBluetoothDeviceDetectedThreadProc), device);
#else
                DynamicEvent.BeginInvoke(BluetoothDeviceDetected, device, new DeviceEventArgs(device));
#endif
		}

#if !PocketPC
        private static void OnUsbDeviceDetected(UsbDevice device)
        {
            if (UsbDeviceDetected != null)
#if PocketPC && !Framework20
				System.Threading.ThreadPool.QueueUserWorkItem(new WaitCallback(OnUsbDeviceDetectedThreadProc), device);
#else
                DynamicEvent.BeginInvoke(UsbDeviceDetected, device, new DeviceEventArgs(device));
#endif
        }
#endif

#if PocketPC && !Framework20
		private static void OnBluetoothDeviceDetectedThreadProc(object state)
		{
			try
			{
				BluetoothDeviceDetected(state, new DeviceEventArgs((Device)state));
			}
			catch
			{
			}
		}
#endif

		/// <summary>Occurs when an attempt is made to identify a device as a GPS device.</summary>
		public static void OnDeviceDetectionAttempted(Device device)
		{
			if (DeviceDetectionAttempted != null)
#if PocketPC && !Framework20
				System.Threading.ThreadPool.QueueUserWorkItem(new WaitCallback(OnDeviceDetectionAttemptedThreadProc), device);			
#else
                DynamicEvent.BeginInvoke(DeviceDetectionAttempted, device, new DeviceEventArgs(device));
#endif
		}

#if PocketPC && !Framework20
		private static void OnDeviceDetectionAttemptedThreadProc(object state)
		{
			try
			{
				DeviceDetectionAttempted(state, new DeviceEventArgs((Device)state));
			}
			catch
			{
			}
		}
#endif

		/// <summary>
		/// Occurs when the device detection system needs to enumerate potential GPS devices on the system.
		/// </summary>
		public static void OnDeviceEnumeration()
		{
			if (DeviceEnumeration != null)
#if PocketPC && !Framework20
				DeviceEnumeration(null, EventArgs.Empty);			
#else
                DynamicEvent.Invoke(DeviceEnumeration, null, EventArgs.Empty);
#endif
		}

		/// <summary>Occurs when the process of detecting GPS devices has begun.</summary>
		public static void OnDeviceDetectionStarted()
		{
			if (DeviceDetectionStarted != null)
#if PocketPC && !Framework20
				System.Threading.ThreadPool.QueueUserWorkItem(new WaitCallback(OnDeviceDetectionStartedThreadProc));			
#else
                DynamicEvent.BeginInvoke(DeviceDetectionStarted, null, EventArgs.Empty);
#endif
		}

#if PocketPC && !Framework20
		private static void OnDeviceDetectionStartedThreadProc(object state)
		{
			try
			{
				DeviceDetectionStarted(null, EventArgs.Empty);
			}
			catch
			{
			}
		}
#endif

		/// <summary>Occurs when an attempt to recognize a device as a GPS device is aborted.</summary>
		public static void OnDeviceDetectionCanceled(Device device)
		{
			if (DeviceDetectionCanceled != null)
#if PocketPC && !Framework20
				System.Threading.ThreadPool.QueueUserWorkItem(new WaitCallback(OnDeviceDetectionCanceledThreadProc), device);			
#else
                DynamicEvent.BeginInvoke(DeviceDetectionCanceled, device, new DeviceEventArgs(device));
#endif
		}

#if PocketPC && !Framework20
		private static void OnDeviceDetectionCanceledThreadProc(object state)
		{
			try
			{
				DeviceDetectionCanceled(state, new DeviceEventArgs((Device)state));
			}
			catch
			{
			}
		}
#endif

		/// <summary>Occurs when GPS device detection has completed.</summary>
		public static void OnDeviceDetectionCompleted()
		{
			if (DeviceDetectionCompleted != null)
#if PocketPC && !Framework20
				System.Threading.ThreadPool.QueueUserWorkItem(new WaitCallback(OnDeviceDetectionCompletedThreadProc));			
#else
                DynamicEvent.BeginInvoke(DeviceDetectionCompleted, null, EventArgs.Empty);
#endif
		}

#if PocketPC && !Framework20
		private static void OnDeviceDetectionCompletedThreadProc(object state)
		{
			try
			{
				DeviceDetectionCompleted(null, EventArgs.Empty);
			}
			catch
			{
			}
		}
#endif

		/// <summary>Occurs when an unhandled exception is thrown during device detection.</summary>
		public static void OnDeviceDetectionExceptionOccurred(object sender, Exception exception)
		{
			if (DeviceDetectionExceptionOccurred != null)
#if PocketPC && !Framework20
				System.Threading.ThreadPool.QueueUserWorkItem(new WaitCallback(OnDeviceDetectionExceptionThreadProc), new ExceptionEventArgs(exception));			
#else
                DynamicEvent.BeginInvoke(DeviceDetectionExceptionOccurred, sender, new ExceptionEventArgs(exception));
#endif
		}

#if PocketPC && !Framework20
		private static void OnDeviceDetectionExceptionThreadProc(object state)
		{
			try
			{
				DeviceDetectionExceptionOccurred(null, (ExceptionEventArgs)state);
			}
			catch
			{
			}
		}
#endif

		/// <summary>
		/// Controls the maximum amount of time to wait for GPS device detection to
		/// complete.
		/// </summary>
		public static TimeSpan DetectionTimeout
		{
			get
			{
				return pDeviceDetectionTimeout;
			}
			set
			{
				if (pDeviceDetectionTimeout.Equals(value)) 
                    return;
				pDeviceDetectionTimeout = value;
			}
		}

		/// <summary>
		/// Controls the maximum amount of time to wait for each device to
		/// complete detection before being aborted.
		/// </summary>
		/// <remarks>This property gives developers the ability to allow more or less time
		/// for detection to complete.  In some cases, more time may be required to discover
		/// a device.  For example, the Hewlett-Packard HW6515 PocketPC needs ten seconds
		/// to initialize its internal GPS device.  Setting this property to a value of
		/// twenty or more seconds is recommended, though it will cause the entire detection 
		/// process to take more time.</remarks>
		public static TimeSpan DetectionTimeoutPerDevice
		{
			get
			{
				return pDeviceDetectionTimeoutPerDevice;
			}
			set
			{
				if (pDeviceDetectionTimeoutPerDevice.Equals(value)) return;
				pDeviceDetectionTimeoutPerDevice = value;
			}
		}

		public static ThreadPriority DetectionThreadPriority
		{
			get
			{
				return pDetectionThreadPriority;
			}
			set
			{
				pDetectionThreadPriority = value;
			}
		}

		/// <summary>Begins scanning the current computer for GPS devices.</summary>
		/// <remarks>
		/// 	<para>This powerful method is very helpful for an application's performance, as
		///     well as simplifying the task of locating GPS devices. When this method is called, a
		///     new thread is launched which analyzes the computer for supported devices, then
		///     examines the data from each device in an attempt to identify GPS data. When the
		///     first device is discovered, the <strong>FirstRespondingGpsDevice</strong> property
		///     is set and the device is also added to the <strong>GpsDevices</strong> collection.
		///     If any additional GPS devices are also located, they are also added to the
		///     <strong>GpsDevices</strong> collection.</para>
		/// 	<para>Other classes such as <strong>GpsStream</strong> work closely with automatic
		///     device detection to simplify the task of processing GPS information. The
		///     <strong>GpsStream</strong> class examines the
		///     <strong>FirstRespondingGpsDevice</strong> property and <strong>GpsDevices</strong>
		///     collection to locate a data source for a GPS interpreter.</para>
		/// 	<para>Since this method is asynchronous and detection is performed in the
		///     background, it is safe to execute this method at any time. Well-written
		///     applications will call this method as early as possible -- such as when the
		///     application first launches -- in order to detect devices as quickly as possible
		///     (see example).</para>
		/// 	<para>On desktop computers, it is common for device detection to complete in under
		///     one second. Mobile device applications, being much slower than desktops, require a
		///     few more seconds to complete, but detection will not interfere with the user
		///     interface. GPS.NET will automatically remember devices which were successfully
		///     detected and try them first the next time device detection is called in order to
		///     improve performance. Device detection will also work successfully with the special
		///     unsupported "Deploy to My Computer" feature of Visual Studio.NET 2005.</para>
		/// 	<para>When working with protocols other than NMEA-0183, it is necessary to register
		///     other interpreters via the <strong>RegisterInterpreter</strong> method before
		///     device detection will recognize other protocols. This is because device detection
		///     relies upon interpreters to perform the actual task of identifying GPS data. For
		///     example, to automatically detect a device using the Garmin® binary protocol,
		///     <strong>RegisterInterpreter</strong> must be called with a
		///     <strong>GarminBinaryInterpreter</strong> object before the <strong>Detect</strong>
		///     method is called.</para>
		/// </remarks>
		/// <example>
		///     This example demonstrates how to improve an application's performance by starting
		///     automatic device detection as early as possible. A call is made to the
		///     <strong>Detect</strong> method before the application's main form is displayed.
		///     Device detection then continues in the background. 
		///     <code lang="VB" title="[New Example]">
		/// ' The main entry point For the application.
		/// Shared Sub Main() 
		///     ' Start device detection In the background
		///     GeoFramework.IO.Devices.Detect()
		///     
		///     ' And run the application As usual
		///     Application.Run(New Form1())
		/// End Sub
		///     </code>
		/// 	<code lang="CS" title="[New Example]">
		/// // The main entry point for the application.
		/// static void Main() 
		/// {
		///     // Start device detection in the background
		///     GeoFramework.IO.Devices.Detect();
		///     
		///     // And run the application as usual
		///     Application.Run(new Form1());
		/// }
		///     </code>
		/// </example>
		public static void Detect()
		{
			try
			{
				// Is it already going?  If so, don't start it again
				if (pIsDetectionInProgress)
					return;

				// If a device is already detected and we only need one, don't detect it again
				if (pIsOnlyFirstDeviceDetected && pGpsDevices.Count > 0)
					return;

				// Flag that we're detecting
				pIsDetectionInProgress = true;

				// Are there any GPS data interpreters registered?  If not, add an NMEA
				// interpreter by default
				if (pInterpreters.Count == 0)
					RegisterInterpreter(typeof(GeoFramework.Gps.Nmea.NmeaInterpreter));

				// Reset both wait events
				DetectionWaitHandle.Reset();
				DeviceDetectedWaitHandle.Reset();

				// Now perform detection
				DetectionThread = new Thread(new ThreadStart(DetectionThreadProc));

                // Set the priority depending on the platform
                switch (Platform.HostPlatformID)
                {
                    case HostPlatformID.Desktop:
                        // Desktop threads should always be at NORMAL!  No change is made.                        
                        break;
                    case HostPlatformID.Smartphone:
                        // Smartphone threads will depend on the default
                        DetectionThread.Priority = pDetectionThreadPriority;
                        break;
                    case HostPlatformID.PocketPC:
                    case HostPlatformID.WindowsCE:
                        // The detection thread will be below normal
                        DetectionThread.Priority = ThreadPriority.BelowNormal;
                        break;
                }

#if !PocketPC || Framework20
                DetectionThread.Name = "GPS.NET GPS Device Detection Manager (http://www.geoframeworks.com)";
                DetectionThread.IsBackground = true;
#endif
				// Start detection
				DetectionThread.Start();

				// Let it start up
				DetectionWaitHandle.WaitOne();
				DetectionWaitHandle.Reset();
			}
			catch
			{
				// Flag that we're detecting
				pIsDetectionInProgress = false;
				throw;
			}
		}

		/// <summary>
		/// Registers a GPS data interpreter with the device detection system.
		/// </summary>
		/// <param name="type">A <strong>Type</strong> of an object deriving from Interpreter which is able 
		/// to recognize raw GPS data.</param>
		/// <remarks><para>GPS devices can be automatically detected if an interpreter is registered
		/// which handles the kind of data output by the device.  For example, if an interpreter
		/// is registered which handles NMEA data, any device transmitting NMEA will be automatically 
		/// detected.  As more interpreters are registered, automatic device detection becomes
		/// more intelligent and can detect a wider variety of devices.</para>
		/// <para>By default, an NmeaInterpreter is automatically registered with the system.
		/// New interpreters should be registered before any call to the <see cref="Detect"></see>
		/// method is made.  To remove an interpreter from device detection, use the
		/// <see cref="UnregisterInterpreter"></see> method.</para></remarks>
		public static void RegisterInterpreter(Type type)
		{
			lock (pInterpreters.SyncRoot)
			{
				foreach (Type existingType in pInterpreters)
				{
					if (existingType.Equals(type))
						return;
				}
				// Continue to add the interpreter
				pInterpreters.Add(type);
			}
            Thread.Sleep(0);
		}

		/// <summary>
		/// Removes a GPS data interpreter from the device detection system.
		/// </summary>
		/// <param name="interpreter">An <strong>Interpreter</strong> object previously
		/// registered to recognize GPS data.</param>
		/// <remarks>If device detection should no longer recognize GPS devices transmitting
		/// a specific data protocol, this method is used.  If a protocol needs to be recognized
		/// later on, it can be re-registered using the <see cref="RegisterInterpreter"></see>
		/// method.</remarks>
		public static void UnregisterInterpreter(Type type)
		{
			lock (pInterpreters.SyncRoot)
			{
				Type TypeToRemove = null;
				foreach (Type existingType in pInterpreters)
				{
					if (existingType.Equals(type))
					{
						TypeToRemove = existingType;
						break;
					}
				}
				if (TypeToRemove != null)
					// Continue to add the interpreter
					pInterpreters.Remove(type);
			}
            Thread.Sleep(0);
		}

#if !PocketPC
        /// <summary>Returns a list of interpreters used to detect devices as GPS devices.</summary>
        [TypeConverter(typeof(ExpandableObjectConverter))]
#endif
		public static ArrayList Interpreters
		{
			get
			{
				return pInterpreters;
			}
		}

        // This is called from RegisterGpsDevice if only one GPS device should be detected
        private static void CancelDetection(object sender)
        {
            CancelDetection();
        }

		/// <summary>Aborts the process of locating GPS devices.</summary>
		public static void CancelDetection()
		{
			// Is detection in progress?
			if (!pIsDetectionInProgress)
				return;

			// Get the list of serial ports
			//ArrayList SerialDevicesToScan = pSerialDevices;
			lock (pSerialDevices.SyncRoot)
			{
				// Scan each device for GPS data
				foreach (SerialDevice device in pSerialDevices)
				{
					if (device.IsDetectionInProgress)
					{
						try
						{
							device.CancelProtocolDetection();
							// Notify of the cancellation
							OnDeviceDetectionCanceled(device);
						}
						catch
						{
							// Continue cancelling
							continue;
						}
					}
				}
			}
			Thread.Sleep(0);

			lock (pBluetoothDevices.SyncRoot)
			{
				// Scan each device for GPS data
				foreach (BluetoothDevice device in pBluetoothDevices)
				{
					if (device.IsDetectionInProgress)
					{
						try
						{
							device.CancelProtocolDetection();
							// Notify of the cancellation
							OnDeviceDetectionCanceled(device);
						}
						catch
						{
							// Continue cancelling
							continue;
						}
					}
				}
			}
			Thread.Sleep(0);

#if !PocketPC || DesignTime || (PocketPC && Framework20)
            // Shut down the detection thread
            try
            {
                if (DetectionThread != null && !DetectionThread.Join(MaximumGracefulShutdownTime))
                    DetectionThread.Abort();
            }
            catch
            {
            }
#else
			// Wait indefinitely for detection to finish
			while(pIsDetectionInProgress)
			{
				Thread.Sleep(100);
				Thread.Sleep(0);
			}
#endif
		}

		/// <summary>
		/// Returns the last GPS device which successfully returned GPS data.
		/// </summary>
		public static Device LastUsedGpsDevice
		{
			get
			{
				return pLastUsedGpsDevice;
			}
		}

		private static void DetectionThreadProc()
		{
			bool IsAborting = false;
			try
			{
				// No.  Flag that it has started
				pIsDetectionComplete = false;

				// Flag that the thread is now active
				if (DetectionWaitHandle == null)
					DetectionWaitHandle = new ManualResetEvent(false);
				else
					DetectionWaitHandle.Set();
				if (DeviceDetectedWaitHandle == null)
					DeviceDetectedWaitHandle = new ManualResetEvent(false);

				// Signal that detection has started
				OnDeviceDetectionStarted();

				// Signal that we need devices to get registered quickly.
				OnDeviceEnumeration();

				#region Scan of GPS Intermediate Driver

#if PocketPC
				// If it's enabled and fully configured, use it!
                if (SharedGpsDevice.IsSupported
                    && pSharedGpsDevice.IsEnabled
                    && pSharedGpsDevice.SourceDevice != null
                    && pSharedGpsDevice.SharedPortName != null
                    && pAllowSharedGpsDeviceConnections)
                {
                    // Reset the collection of devices
                    lock (pGpsDevices.SyncRoot)
                    {
                        pGpsDevices.Clear();
                    }
                    lock (pSerialGpsDevices.SyncRoot)
                    {
                        pSerialGpsDevices.Clear();
                    }
                    lock (pBluetoothGpsDevices.SyncRoot)
                    {
                        pBluetoothGpsDevices.Clear();
                    }

                    // Register the GPS device for NMEA use
                    RegisterGpsDevice(pSharedGpsDevice, GeoFramework.Gps.Nmea.NmeaInterpreter.ProtocolName);

                    // And there's no need to perform any other scanning!
                    return;
                }
                else
                {
                    // If the shared GPS device is present but disabled, disallow it
                    if (pGpsDevices.Contains(pSharedGpsDevice))
                    {
                        lock (pGpsDevices.SyncRoot)
                        {
                            pGpsDevices.Remove(pSharedGpsDevice);
                        }
                    }

                    // Continue normal scanning
                }
#endif

				#endregion

				#region Scan of HW6515 Internal GPS

#if PocketPC && !Smartphone

                /* The Hewlett-Packard 6515 has an integrated GPS device.  The problem
			     * with the 6515 is that calls to Bluetooth connections cause a system
			     * message box to be displayed.  So, disable Bluetooth connections on
			     * the 651x series by default.
			     */
                if (Environment.OSVersion.Platform == PlatformID.WinCE)
                {
                    if (HewlettPackard.Ipaq.IsSupported && HewlettPackard.Ipaq.IsIntegratedGpsDeviceAllowed)
                    {
                        RegisterGpsDevice(HewlettPackard.Ipaq.IntegratedGpsDevice);
                    }
                }
#endif

				#endregion

				#region Scan of Last-Known Device

				if (pIsDeviceCacheEnabled && pLastUsedGpsDevice != null)
				{
					if ((pLastUsedGpsDevice is SerialDevice && pAllowSerialConnections)
						|| (pLastUsedGpsDevice is BluetoothDevice && pAllowBluetoothConnections)
#if !PocketPC
                        || (pLastUsedGpsDevice is UsbDevice && pAllowUsbConnections)
#endif
						)
					{
                        // If this is a serial device, check to see if the port should be avoided
                        if (pLastUsedGpsDevice is SerialDevice)
                        {
                            SerialDevice LastUsedSerialDevice = pLastUsedGpsDevice as SerialDevice;

                            // Lock the avoided serial port collection during the scan
                            lock (pAvoidedSerialPorts.SyncRoot)
                            {
                                // Is the port being avoided?
                                if (!pAvoidedSerialPorts.Contains(LastUsedSerialDevice.Port))
                                {
                                    // No. continue detection
                                    pLastUsedGpsDevice.DetectProtocol();
                                }
                            }
                        }
                        else
                        {
                            // Start detection for this device
                            pLastUsedGpsDevice.DetectProtocol();
                        }

#if PocketPC
						if (Environment.OSVersion.Platform == PlatformID.WinCE)
						{
							// On the PocketPC, resources are limited, so
							// wait for this attempt to finish before trying more
							pLastUsedGpsDevice.WaitForProtocolDetection();
						}
#endif
					}
				}

				#endregion

#if !PocketPC
                #region Scan of USB Devices

                // Start scanning for USB devices
                ArrayList UsbDevicesToScan = null;
                if (pAllowUsbConnections)
                {
                    #region Enumeration of USB Devices

                    // Make a list of devices to scan
                    UsbDevicesToScan = UsbDevices.Clone() as ArrayList;

                    #endregion

                    foreach (UsbDevice device in UsbDevicesToScan)
                    {
                        device.DetectProtocol();
                    }
                }

                #endregion

#endif

                #region Scan of Serial Devices

                /* Serial devices will be scanned so long as serial connections are
                 * allowed.  Scanning will also be skipped if the GID shared-GPS driver
                 * is active.
                 */
                bool ScanSerialDevices = pAllowSerialConnections;
                ArrayList SerialDevicesToScan = null;

                if (ScanSerialDevices)
                {
                    #region Enumeration of Serial Devices

                    // Make a list of devices to scan

                    // Is exhaustive serial scanning enabled?  If so, scan everything
                    // from COM1: to COM20:
                    SerialDevicesToScan = SerialDevices.Clone() as ArrayList;

#if !Smartphone
                    if (pIsExhaustiveDetectionEnabled)
                    {
                        for (int i = 0; i <= MaximumSerialPortToScan; i++)
                        {
                            bool IsFound = false;
                            foreach (SerialDevice item in SerialDevicesToScan)
                            {
                                if ((int)item.Port == i)
                                {
                                    IsFound = true;
                                    break;
                                }
                            }
                            if (!IsFound)
                            {
                                SerialDevicesToScan.Add(
                                    new SerialDevice((CommunicationPort)i));
                            }
                        }
                    }
#endif


                    #endregion

                    // Lock the avoided serial port collection
                    lock (pAvoidedSerialPorts.SyncRoot)
                    {
                        foreach (SerialDevice device in SerialDevicesToScan)
                        {
                            // Is this device mapped to the virtual Bluetooth serial port?
                            // If so, and BT connections aren't allowed, skip it!
                            if (device.IsBluetoothDevice && !AllowBluetoothConnections)
                                continue;

                            // Is this port being avoided?
                            if (!pAvoidedSerialPorts.Contains(device.Port))
                            {
                                // No.  Proceed to scan it
                                device.DetectProtocol();
                            }
                        }
                    }
                }

                #endregion

                #region Scan of Bluetooth Devices

                /* Bluetooth devices will be scanned so long as BT connections are
                 * allowed.  Scanning will also be skipped if the GID shared-GPS driver
                 * is active.
                 */
                bool ScanBluetoothDevices = pAllowBluetoothConnections;
                ArrayList BluetoothDevicesToScan = null;

#if PocketPC
                if (SharedGpsDevice.IsSupported && pSharedGpsDevice.IsEnabled)
                    ScanBluetoothDevices = false;
#endif

				// Start scanning for Bluetooth devices
                if (ScanBluetoothDevices)
				{

                    #region Enumeration of Bluetooth Devices

                    // Make a list of devices to scan
                    BluetoothDevicesToScan = pBluetoothDevices.Clone() as ArrayList;

                    #endregion


					foreach (BluetoothDevice device in BluetoothDevicesToScan)
					{
						device.DetectProtocol();
					}

                    //// Also perform regular detection
                    //DiscoverBluetoothDevices();

				}

                #endregion

#if !PocketPC
				// Start scanning for Bluetooth devices
                if (pAllowUsbConnections)
                {


                    foreach (UsbDevice device in UsbDevicesToScan)
                    {
                        device.WaitForProtocolDetection();
                    }
                }
#endif

                #region Wait for all scanning to complete

                // Wait for serial devices to finish responding
                if (ScanSerialDevices)
				{
					foreach (SerialDevice device in SerialDevicesToScan)
					{
						device.WaitForProtocolDetection();
					}
				}

				// Wait for serial devices to finish responding
                if (ScanBluetoothDevices)
				{
					foreach (BluetoothDevice device in BluetoothDevicesToScan)
					{
						device.WaitForProtocolDetection();
					}
				}

				#endregion
			}

#if !PocketPC || Framework20
            catch (ThreadAbortException)
            {
                // Just exit!
                IsAborting = true;
            }
#endif
			catch (Exception ex)
			{
				// Use the device detection event to raise the error since this occurs on a separate thread
				OnDeviceDetectionExceptionOccurred(null, ex);
			}
			finally
			{
				// And clear flags
				pIsDetectionInProgress = false;
				pIsDetectionComplete = true;

				// Finally, flag that detection has completed.
#if Framework20 && !PocketPC
                if (!DeviceDetectedWaitHandle.SafeWaitHandle.IsInvalid)
#else
                if (DeviceDetectedWaitHandle.Handle != new IntPtr(-1))
#endif
				{
					try
					{
						DeviceDetectedWaitHandle.Set();
					}
					catch (ObjectDisposedException)
					{
					}
				}

				// Signal that detection has completed
				if (!IsAborting)
					OnDeviceDetectionCompleted();

				// Whee!
				//GC.Collect();
				//GC.WaitForPendingFinalizers();

			}
		}

//        public static void DiscoverBluetoothDevices()
//        {
//            #region New Code

//#if Smartphone && !Framework20
//            // Reset the discovery wait handle
//            DiscoveryWaitHandle.Reset();
//#endif

//            // If Bluetooth isn't supported, do nothing
//            if (!BluetoothRadio.IsSupported)
//                return;

//            // Enable Bluetooth
//            try
//            {
//                IsBluetoothEnabled = true;
//            }
//            catch (NotSupportedException)
//            {
//                // NEW: 5/22/06.  Don't check for any device if BT isn't supported
//                return;
//            }

//            //// Now scan for devices
//            //BTHNS_INQUIRYBLOB bib = null;
//            //BLOB b = null;


//            try
//            {
//                // Bluetooth and WSAQUERYSET
//                // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/bluetooth/bluetooth/bluetooth_and_wsaqueryset_for_service_inquiry.asp

//                // Create a new query
//                WSAQUERYSET Query = new WSAQUERYSET();
//                Query.dwSize = 60; // Marshal.SizeOf(Query);

//                // A value of 16 corresponds to NS_BTH
//                Query.dwNameSpace = 16;

//                //// Allocate a pointer for the GUID
//                //Query.lpServiceClassId = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Guid)));
//                //// Copy the virtual serial port GUID in
//                //Marshal.StructureToPtr(BluetoothDevice.VirtualSerialPortServiceGuid, Query.lpServiceClassId, false);

//                // Create a new inquiry BLOB
//                //BTHNS_INQUIRYBLOB InquiryBlob = new BTHNS_INQUIRYBLOB(5, (short)5);
//                //BLOB blob = new BLOB(InquiryBlob);
//                //Query.lpBlob = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(BLOB)));
//                //Marshal.StructureToPtr(blob, querySet.lpBlob, false);

//                //// Get a pointer to the query set
//                //IntPtr qryptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(WSAQUERYSET)));
//                //Marshal.StructureToPtr(querySet, qryptr, false);

//                // Look up the first service
//                IntPtr hLookup = IntPtr.Zero;
//                int err = Bluetooth.NativeMethods.LookupServiceBegin(Query, LookupFlags.LUP_RETURN_ALL, out hLookup);
//                if (err != 0)
//                {
//                    throw new System.Net.Sockets.SocketException(Bluetooth.NativeMethods.WsaGetLastError());
//                }

//                if (hLookup != IntPtr.Zero) 
//                    Bluetooth.NativeMethods.LookupServiceEnd(hLookup);

//                /*
//                // This value must be zero
//                Query.dwNumberOfCsAddrs = 0;

//                // Now scan for devices
//                int discoveredDevices = 0;
//                int lookupresult = -1;
//                Int32 handle = 0;

//                //int handle = 0;
//                byte[] buffer = new byte[1024];
//                int bufferlen = buffer.Length;

//                //BitConverter.GetBytes((int)60).CopyTo(buffer, 0);
//                //BitConverter.GetBytes((int)16).CopyTo(buffer, 20);
//                //int lookupresult = 0;

//                //bib = new BTHNS_INQUIRYBLOB(5, (short)5);                
//                //b = new BLOB(bib);

//                //    GCHandle hBlob = GCHandle.Alloc(b.ToByteArray(), GCHandleType.Pinned);

//                //    BitConverter.GetBytes(hBlob.AddrOfPinnedObject().ToInt32() + 4).CopyTo(buffer, 56);

//                try
//                {
//                    //start looking for Bluetooth devices
//                    lookupresult = Bluetooth.NativeMethods.LookupServiceBegin(Query, LookupFlags.Containers, ref handle);
//                }
//                catch (NotSupportedException)
//                {
//                    // Bluetooth scanning is not supported

//                }
//                catch
//                {
//                }
//                finally
//                {
//                    //// Free up the blob
//                    //if (hBlob.IsAllocated)
//                    //    hBlob.Free();
//                }

//                while (discoveredDevices < 5 && lookupresult != -1)
//                {
//                    lookupresult = Bluetooth.NativeMethods.LookupServiceNext(handle, LookupFlags.ReturnAddr | LookupFlags.ReturnName, ref bufferlen, buffer);
//                    if (lookupresult != -1)
//                    {
//                        //increment found count
//                        discoveredDevices++;

//                        //pointer to outputbuffer
//                        int bufferptr = BitConverter.ToInt32(buffer, 48);
//                        //remote socket address
//                        int sockaddrptr = Marshal.ReadInt32((IntPtr)bufferptr, 8);
//                        //remote socket len
//                        int sockaddrlen = Marshal.ReadInt32((IntPtr)bufferptr, 12);


//                        SocketAddress btsa = new SocketAddress(AddressFamily.Unspecified, sockaddrlen);

//                        for (int sockbyte = 0; sockbyte < sockaddrlen; sockbyte++)
//                        {
//                            btsa[sockbyte] = Marshal.ReadByte((IntPtr)sockaddrptr, sockbyte);
//                        }


//                        BluetoothEndPoint bep = new BluetoothEndPoint(null, Guid.Empty);
//                        bep = (BluetoothEndPoint)bep.Create(btsa);
//                        bep.Service = BluetoothDevice.VirtualSerialPortServiceGuid;
//                        //bep.Address = btsa;

//                        // Check if the device is already discovered
//                        bool IsAlreadyRecorded = false;
//                        lock (pBluetoothDevices.SyncRoot)
//                        {
//                            BluetoothDevice NewDevice = null;
//                            foreach (BluetoothDevice ExistingDevice in pBluetoothDevices)
//                            {
//                                // Was it found?
//                                if (bep.Address.Equals(ExistingDevice.Address))
//                                {
//                                    // Yes.  We won't need to add it again
//                                    IsAlreadyRecorded = true;
//                                    NewDevice = ExistingDevice;
//                                    break;
//                                }

//                            }
//                            if (!IsAlreadyRecorded)
//                            {
//                                // Create a new bluetooth device
//                                NewDevice = new BluetoothDevice(bep.Address, Marshal.PtrToStringUni((IntPtr)BitConverter.ToInt32(buffer, 4)));

//                                // Add it to the list
//                                pBluetoothDevices.Add(NewDevice);
//                            }

//                            // And attempt to detect the protocol now
//                            NewDevice.DetectProtocol();
//                        }
//                        Thread.Sleep(0);

//                        //					//new deviceinfo
//                        //					BluetoothDeviceInfo newdevice;
//                        //
//                        //					if(System.Environment.OSVersion.Platform == PlatformID.WinCE)
//                        //					{
//                        //						newdevice = new BluetoothDevice(bep.Address, Marshal.PtrToStringUni((IntPtr)BitConverter.ToInt32(buffer, 4)));
//                        //					}
//                        //					else
//                        //					{						
//                        //						newdevice = new BluetoothDevice(bep.Address, Marshal.PtrToStringUni((IntPtr)BitConverter.ToInt32(buffer, 4)));
//                        //					}

//                        // Add to discovered list
//                        //al.Add(newdevice);

//                    }

//                }

//                // Stop looking for devices
//                lookupresult = Bluetooth.NativeMethods.LookupServiceEnd(handle);
//                */

//            }
//            catch
//            {
//                throw;
//            }
//            finally
//            {
//                // Dispose of all blob and related objects
//                //if (bib != null)
//                //    bib.Dispose();
//                //if (b != null)
//                //    b.Dispose();

//                //GC.Collect();
//                //GC.WaitForPendingFinalizers();

//#if Smartphone && !Framework20
//                if(DiscoveryWaitHandle.Handle != new IntPtr(-1))
//                {
//                    try
//                    {
//                        DiscoveryWaitHandle.Set();
//                    }
//                    catch
//                    {
//                    }
//                }
//#endif
//            }
//            //return results
//            //return (BluetoothDeviceInfo[])al.ToArray(typeof(BluetoothDeviceInfo));
//            #endregion

//            #region Old CRASHING Code

////#if Smartphone
////            // Reset the discovery wait handle
////            DiscoveryWaitHandle.Reset();
////#endif

////            try
////            {
////                // Now scan for devices
////                int discoveredDevices = 0;
////                int handle = 0;

////                byte[] buffer = new byte[1024];
////                BitConverter.GetBytes((int)60).CopyTo(buffer, 0);
////                BitConverter.GetBytes((int)16).CopyTo(buffer, 20);

////                int bufferlen = buffer.Length;

////                int lookupresult = 0;

////                BTHNS_INQUIRYBLOB bib = new BTHNS_INQUIRYBLOB(5, (short)5);


////                BLOB b = new BLOB(bib);

////                //GCHandle hBlob = GCHandle.Alloc(b.ToByteArray(), GCHandleType.Pinned);


////                BitConverter.GetBytes(hBlob.AddrOfPinnedObject().ToInt32() + 4).CopyTo(buffer, 56);


////                //start looking for Bluetooth devices
////#if !Framework10
////                if (System.Environment.OSVersion.Platform == PlatformID.WinCE)
////                {
////                    lock (BluetoothStream.BluetoothSyncRoot)
////                    {
////                        //lookupresult = Bluetooth.NativeMethods.CeLookupServiceBegin(buffer, LookupFlags.Containers, ref handle);
////                    }
////                }
////                else
////                {
////#endif
////                    //lookupresult = Bluetooth.NativeMethods.XpLookupServiceBegin(buffer, LookupFlags.Containers | LookupFlags.FlushCache, ref handle);
////#if !Framework10
////                }
////#endif

////                //hBlob.Free();

////                while (discoveredDevices < 5 && lookupresult != -1)
////                {
////#if !Framework10
////                    if (System.Environment.OSVersion.Platform == PlatformID.WinCE)
////                    {
////                        lock (BluetoothStream.BluetoothSyncRoot)
////                        {
////                            //lookupresult = Bluetooth.NativeMethods.CeLookupServiceNext(handle, LookupFlags.ReturnAddr | LookupFlags.ReturnName, ref bufferlen, buffer);
////                        }
////                        Thread.Sleep(0);
////                    }
////                    else
////                    {
////#endif
////                        //lookupresult = Bluetooth.NativeMethods.XpLookupServiceNext(handle, LookupFlags.ReturnAddr | LookupFlags.ReturnName, ref bufferlen, buffer);
////#if !Framework10
////                    }
////#endif


////                    if (lookupresult != -1)
////                    {
////                        //increment found count
////                        discoveredDevices++;


////                        //pointer to outputbuffer
////                        int bufferptr = BitConverter.ToInt32(buffer, 48);
////                        //remote socket address
////                        int sockaddrptr = Marshal.ReadInt32((IntPtr)bufferptr, 8);
////                        //remote socket len
////                        int sockaddrlen = Marshal.ReadInt32((IntPtr)bufferptr, 12);


////                        SocketAddress btsa = new SocketAddress(AddressFamily.Unspecified, sockaddrlen);

////                        for (int sockbyte = 0; sockbyte < sockaddrlen; sockbyte++)
////                        {
////                            btsa[sockbyte] = Marshal.ReadByte((IntPtr)sockaddrptr, sockbyte);
////                        }

////                        BluetoothEndPoint bep = new BluetoothEndPoint(null, Guid.Empty);
////                        bep = (BluetoothEndPoint)bep.Create(btsa);

////                        // Check if the device is already discovered
////                        bool IsAlreadyRecorded = false;
////                        lock (pBluetoothDevices.SyncRoot)
////                        {
////                            BluetoothDevice NewDevice = null;
////                            foreach (BluetoothDevice ExistingDevice in pBluetoothDevices)
////                            {
////                                // Was it found?
////                                if (bep.Address.Equals(ExistingDevice.Address))
////                                {
////                                    // Yes.  We won't need to add it again
////                                    IsAlreadyRecorded = true;
////                                    NewDevice = ExistingDevice;
////                                    break;
////                                }

////                            }
////                            if (!IsAlreadyRecorded)
////                            {
////                                // Create a new bluetooth device
////                                NewDevice = new BluetoothDevice(bep.Address, Marshal.PtrToStringUni((IntPtr)BitConverter.ToInt32(buffer, 4)));

////                                // Add it to the list
////                                pBluetoothDevices.Add(NewDevice);
////                            }

////                            // And attempt to detect the protocol now
////                            NewDevice.DetectProtocol();
////                        }
////                        Thread.Sleep(0);

////                        //					//new deviceinfo
////                        //					BluetoothDeviceInfo newdevice;
////                        //
////                        //					if(System.Environment.OSVersion.Platform == PlatformID.WinCE)
////                        //					{
////                        //						newdevice = new BluetoothDevice(bep.Address, Marshal.PtrToStringUni((IntPtr)BitConverter.ToInt32(buffer, 4)));
////                        //					}
////                        //					else
////                        //					{						
////                        //						newdevice = new BluetoothDevice(bep.Address, Marshal.PtrToStringUni((IntPtr)BitConverter.ToInt32(buffer, 4)));
////                        //					}

////                        // Add to discovered list
////                        //al.Add(newdevice);

////                    }
////                }

////                //stop looking
////#if !Framework10
////                if (System.Environment.OSVersion.Platform == PlatformID.WinCE)
////                {
////                    lock (BluetoothStream.BluetoothSyncRoot)
////                    {
////                        //lookupresult = Bluetooth.NativeMethods.CeLookupServiceEnd(handle);
////                    }
////                }
////                else
////                {
////#endif
////                    //lookupresult = Bluetooth.NativeMethods.XpLookupServiceEnd(handle);
////#if !Framework10
////                }
////#endif
////            }
////            catch
////            {
////                throw;
////            }
////            finally
////            {
////#if Smartphone
////                if(DiscoveryWaitHandle != null)
////                {
////                    try
////                    {
////                        DiscoveryWaitHandle.Set();
////                    }
////                    catch
////                    {
////                    }
////                }
////#endif
////            }
////            //return results
////            //return (BluetoothDeviceInfo[])al.ToArray(typeof(BluetoothDeviceInfo));


//            #endregion
//        }

		/// <summary>
		/// Controls whether the Bluetooth® radio is operational.
		/// </summary>
		/// <remarks>
		/// <para>There are several manufacturers which license Bluetooth® software
		/// and provide their own implementation on mobile devices, as well as desktop computers.
		/// As a result, each computer may have a different API for controlling Bluetooth®
		/// operations.</para>
		/// <para>On computers running Microsoft Bluetooth software, this property will successfully
		/// return whether the radio is turned on, or change its status.  A value of <strong>True</strong>
		/// will activate Bluetooth and allow communication with Bluetooth devices.</para>
		/// <para>If the Bluetooth stack is not supported, this property will raise a <strong>NotSupportedException</strong>.
		/// Plans are under way to simplify Bluetooth communications across multiple Bluetooth stacks.</para>
		/// </remarks>
		public static bool IsBluetoothEnabled
		{
			get
			{
				try
				{
					if(!BluetoothRadio.IsSupported)
						return false;

					return BluetoothRadio.PrimaryRadio.Mode == RadioMode.Connectable;
				}
				catch
				{
					throw new NotSupportedException("The Bluetooth software on this machine is by a manufacturer which is not yet supported by GPS.NET.   Bluetooth status cannot be determined.");
				}
			}
			set
			{
				try
				{
					// Is Bluetooth supported?
					if(!BluetoothRadio.IsSupported)
						return;


					if (value)
						BluetoothRadio.PrimaryRadio.Mode = RadioMode.Connectable;
					else
						BluetoothRadio.PrimaryRadio.Mode = RadioMode.PowerOff;
				}
				catch
				{
					throw new NotSupportedException("The Bluetooth software on this machine is by a manufacturer which is not yet supported by GPS.NET.   Bluetooth status cannot be determined.");
				}
			}
		}

        
		public static void RefreshBluetoothDevices()
		{
			#region Device Hints

			// If device detection completed earlier, the device hint
			// will exist.  This will speed up detection.
			RegistryKey DeviceHints = null;
			try
			{
				DeviceHints = Registry.CurrentUser.OpenSubKey(@"Software\GeoFrameworks\GPS.NET\2.0\Bluetooth Devices", false);
				if (DeviceHints != null)
				{
					// Get the list of COM ports
					string[] SubKeyNames = DeviceHints.GetSubKeyNames();
					// Now make a device out of each
					foreach (string value in SubKeyNames)
					{
						RegistryKey SubKey = null; 
						try
						{							
							SubKey = DeviceHints.OpenSubKey(value, false);
							if(SubKey == null)
								continue;

							// Read the name and address of the device
							string Name = SubKey.GetValue("Name") as string;
							string AddressValue = SubKey.GetValue("Address") as string;
							// If we have a value, use it
							BluetoothAddress Address = BluetoothAddress.Parse(AddressValue);
							// Add the port to the list if it's not already in there
							bool IsAlreadyRecorded = false;
							lock (pBluetoothDevices.SyncRoot)
							{
								// Try to find the device in the existing list
								foreach (BluetoothDevice device in pBluetoothDevices)
								{
									// Was it found?
									if (device.Address.Equals(Address))
									{
										// Yes.  We won't need to add it again
										IsAlreadyRecorded = true;
										break;
									}
								}

								// Was something found?
								if (!IsAlreadyRecorded)
								{
									BluetoothDevice NewDevice = new BluetoothDevice(Address, Name);
									// Flag that this has been detected before
									NewDevice.SetIsPreviouslyDetected(true);
									pBluetoothDevices.Add(NewDevice);
								}
							}
							Thread.Sleep(0);
						}
						catch
						{
							try
							{
								DeviceHints.DeleteValue(value);
							}
							catch
							{
							}
							continue;
						}
						finally
						{
							if(SubKey != null)
								SubKey.Close();
						}
					}
				}
			}
			catch
			{
				// Ignore errors during registry scanning
			}
			finally
			{
				if (DeviceHints != null)
					DeviceHints.Close();
			}

			#endregion


#if !Framework10
			if (Environment.OSVersion.Platform == PlatformID.WinCE)
			{
				//open bluetooth device key
				RegistryKey devkey = null;

				#region Microsoft Bluetooth Registry Analysis

				try
				{
					devkey = Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\Bluetooth\Device", false);
					if (devkey != null)
					{
						//enumerate the keys
						foreach (string devid in devkey.GetSubKeyNames())
						{
							//get friendly name
							RegistryKey thisdevkey = null;
							string name;
							try
							{
								thisdevkey = devkey.OpenSubKey(devid);
								name = thisdevkey.GetValue("name").ToString();
							}
							catch
							{
								continue;
							}
							finally
							{
								if (thisdevkey != null)
									thisdevkey.Close();
							}

							//add to collection
							BluetoothDevice thisdevice = new BluetoothDevice(BluetoothAddress.Parse(devid), name);
							bool IsAlreadyRecorded = false;
							lock (pBluetoothDevices.SyncRoot)
							{
								foreach (BluetoothDevice ExistingDevice in pBluetoothDevices)
								{
									// Was it found?
									if (thisdevice.Address.Equals(ExistingDevice.Address))
									{
										// Yes.  We won't need to add it again
										IsAlreadyRecorded = true;
										break;
									}
								}
								if (!IsAlreadyRecorded)
								{
									pBluetoothDevices.Add(thisdevice);
								}
							}
							Thread.Sleep(0);
						}
					}
				}
				catch
				{
					// Ignore analysis errors
				}
				finally
				{
					if (devkey != null)
						devkey.Close();
				}
				#endregion
			}
#endif

			// Whee!
			//GC.Collect();
			//GC.WaitForPendingFinalizers();

		}

		public static void AddDevice(Device device)
		{
			// Add the port to the list if it's not already in there
			bool IsAlreadyRecorded = false;

			if (device is SerialDevice)
			{
				SerialDevice TheDevice = device as SerialDevice;
				lock (pSerialDevices.SyncRoot)
				{
					// Try to find the device in the existing list
					foreach (SerialDevice ExistingDevice in pSerialDevices)
					{
						// Was it found?
						if (ExistingDevice.Port == TheDevice.Port)
						{
							// Yes.  We won't need to add it again
							IsAlreadyRecorded = true;
							break;
						}
					}
					// Was something found?
					if (!IsAlreadyRecorded)
					{
						pSerialDevices.Add(TheDevice);
					}
				}
				Thread.Sleep(0);
			}
			else if (device is BluetoothDevice)
			{
				BluetoothDevice TheDevice = device as BluetoothDevice;
				lock (pBluetoothDevices.SyncRoot)
				{
					// Try to find the device in the existing list
					foreach (BluetoothDevice ExistingDevice in pBluetoothDevices)
					{
						// Was it found?
						if (ExistingDevice.Address.Equals(TheDevice.Address))
						{
							// Yes.  We won't need to add it again
							IsAlreadyRecorded = true;
							break;
						}
					}
					// Was something found?
					if (!IsAlreadyRecorded)
					{
						pBluetoothDevices.Add(TheDevice);
					}
				}
				Thread.Sleep(0);
			}
			else
			{
				throw new FormatException("The specified device object was not recognized as either a serial or Bluetooth device.  GPS.NET does not know how to categorize this object.");
			}
		}

		///// <summary>
		///// Rebuilds the list of available USB devices connected to the computer.
		///// </summary>
		//public static void RefreshUsbDevices()
		//{
		//    On
		//    // Look for Garmin USB devices
		//    UsbDeviceClass GarminUsbClass = new UsbDeviceClass("{2C9C45C2-8E7D-4C08-A12D-816BBAE722C0}");

		//    lock (pUsbDevices.SyncRoot)
		//    {
		//        foreach (UsbDevice device in GarminUsbClass.Devices)
		//        {
		//            // Skip any USB device with no path
		//            if (device.Path == null)
		//                continue;

		//            // Search for existing USB devices
		//            bool IsAlreadyRecorded = false;
		//            foreach (UsbDevice ExistingDevice in pUsbDevices)
		//            {
		//                // Was it found?
		//                if (ExistingDevice.Path.Equals(device.Path))
		//                {
		//                    // Yes.  We won't need to add it again
		//                    IsAlreadyRecorded = true;
		//                    break;
		//                }
		//            }
		//            // Was something found?
		//            if (!IsAlreadyRecorded)
		//            {
		//                pUsbDevices.Add(device);
		//            }
		//        }
		//    }
		//}

		/// <summary>Rebuilds the list of available serial ports on the computer.</summary>
		public static void RefreshSerialDevices()
		{


#if Smartphone
#if Framework20
			// Add the serial port
            RegistryKey IncomingPort = null;
            try
            {
                IncomingPort = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Bluetooth\Serial\Ports", false);
                if (IncomingPort != null)
                {
                    // Enumerate sub-keys
                    foreach (string subKey in IncomingPort.GetSubKeyNames())
                    {
                        // If it's "Incoming Port", skip it
                        if (subKey == "Incoming")
                            continue;

                        // Otherwise, this is a device!  
                        RegistryKey DeviceKey = null;
                        try
                        {
                            DeviceKey = IncomingPort.OpenSubKey(subKey, false);
                            if (DeviceKey != null)
                            {
                                // Get the port name
                                string BluetoothPortName = DeviceKey.GetValue("Port") as string;
                                if (BluetoothPortName == null)
                                    return;

                                // Convert the port name to an integer
                                int PortNumber = Convert.ToInt32(BluetoothPortName.Substring(3));

                                // Add the port to the list if it's not already in there
                                bool IsAlreadyRecorded = false;
                                lock (pSerialDevices.SyncRoot)
                                {
                                    // Try to find the device in the existing list
                                    foreach (SerialDevice device in pSerialDevices)
                                    {
                                        // Was it found?
                                        if ((int)device.Port == PortNumber)
                                        {
                                            // Yes.  We won't need to add it again
                                            IsAlreadyRecorded = true;
                                            return;
                                        }
                                    }
                                    // Was something found?
                                    if (!IsAlreadyRecorded)
                                    {
                                        SerialDevice BluetoothSerial = new SerialDevice("COM" + PortNumber.ToString() + ":", BaudRate.Baud115200);
                                        pSerialDevices.Add(BluetoothSerial);
                                    }
                                }
                                Thread.Sleep(0);
                            }
                        }
                        catch
                        {
                        }
                        finally
                        {
                            if (DeviceKey != null)
                                DeviceKey.Close();
                        }
                    }
                }
            }
            catch
            {
            }
            finally
            {
                if (IncomingPort != null)
                    IncomingPort.Close();
            }

#else
            // On Smartphone on Compact Framework 1.0, serial connections are forbidden!
#endif
#else

            #region Hewlett-Packard 651x Integrated GPS Device

#if PocketPC && !Smartphone
			/* On the Hewlett-Packard HW6515, there is a built-in GPS device
				 * on COM7:.  Use this device!
				 */
			if (Environment.OSVersion.Platform == PlatformID.WinCE)
			{
				if (HewlettPackard.Ipaq.IsSupported)
				{
					// Add the port to the list if it's not already in there
					bool IsAlreadyRecorded = false;
					lock (pSerialDevices.SyncRoot)
					{
						// Try to find the device in the existing list
						foreach (SerialDevice device in pSerialDevices)
						{
							// Was it found?
							if (device.Port == HewlettPackard.Ipaq.IntegratedGpsDevice.Port)
							{
								// Yes.  We won't need to add it again
								IsAlreadyRecorded = true;
								return;
							}
						}
						// Was something found?
						if (!IsAlreadyRecorded)
						{
							pSerialDevices.Add(HewlettPackard.Ipaq.IntegratedGpsDevice);
						}
					}
					Thread.Sleep(0);
                    
					// And stop enumeration here
					return;
				}
			}
#endif
            #endregion

            #region Device Hints

            // If device detection completed earlier, the device hint
            // will exist.  This will speed up detection.
            RegistryKey DeviceHints = null;
            try
            {
                DeviceHints = Registry.CurrentUser.OpenSubKey(@"Software\GeoFrameworks\GPS.NET\2.0\Serial Devices", false);
                if (DeviceHints != null)
                {
                    // Get the sub-keys
                    foreach (string subKeyName in DeviceHints.GetSubKeyNames())
                    {
                        // Open the sub-key, which contains the device name
                        RegistryKey SubKey = null;
                        try
                        {
                            SubKey = DeviceHints.OpenSubKey(subKeyName);
                            if (SubKey == null)
                                continue;

                            // Load the port, baud rate, and more
                            string PortNameValue = Convert.ToString(SubKey.GetValue("Port Name"));
                            int BaudRateValue = Convert.ToInt32(SubKey.GetValue("Baud Rate", 4800));
                            int DataBitsValue = Convert.ToInt32(SubKey.GetValue("Data Bits", 8));
                            int StopBitsValue = Convert.ToInt32(SubKey.GetValue("Stop Bits", 1));

                            // Add the port to the list if it's not already in there
                            bool IsAlreadyRecorded = false;
                            lock (pSerialDevices.SyncRoot)
                            {
                                // Try to find the device in the existing list
                                foreach (SerialDevice device in pSerialDevices)
                                {
                                    // Was it found?
                                    if (device.PortName == PortNameValue)
                                    {
                                        // Yes.  We won't need to add it again
                                        IsAlreadyRecorded = true;
                                        break;
                                    }
                                }

                                // Was something found?
                                if (!IsAlreadyRecorded)
                                {
                                    // Yes. Get the baud rate as well                                    
                                    SerialDevice NewDevice =
                                        new SerialDevice(PortNameValue, (BaudRate)BaudRateValue,
                                                (DataBits)DataBitsValue, Parity.None, (StopBits)StopBitsValue,
                                                FlowControl.None);
                                    // Flag that this has been detected before
                                    NewDevice.SetIsPreviouslyDetected(true);
                                    // Add it to the collection
                                    pSerialDevices.Add(NewDevice);
                                }
                            }
                            Thread.Sleep(0);
                        }
                        catch
                        {
                            // Close the key if needed
                            if (SubKey != null)
                                SubKey.Close();

                            // Delete the sub-key entirely
                            DeviceHints.DeleteSubKeyTree(subKeyName);
                        }
                        finally
                        {
                            if (SubKey != null)
                                SubKey.Close();
                        }
                    }
                }
            }
            catch
            {
                // Ignore errors during refreshing
            }
            finally
            {
                if (DeviceHints != null)
                    DeviceHints.Close();
            }

			#endregion

			#region Widcomm Registry Analysis
            // Look for WidComm Bluetooth devices
            RegistryKey Widcomm = null;
            try
            {
#if !Framework10
                if (Environment.OSVersion.Platform == PlatformID.WinCE)
                {
                    Widcomm = Registry.LocalMachine.OpenSubKey(@"Software\Widcomm\Connections", false);
                }
                else
                {
#endif
                    Widcomm = Registry.CurrentUser.OpenSubKey(@"Software\Widcomm\Connections", false);
#if !Framework10
                }
#endif

                if (Widcomm != null)
                {
                    // Enumerate all WIDCOMM devices
                    string[] SubKeys = Widcomm.GetSubKeyNames();
                    foreach (string subKey in SubKeys)
                    {
                        // Open the sub-key  
                        RegistryKey WidcommConnectionKey = Widcomm.OpenSubKey(subKey);
                        try
                        {
                            // Skip if this key can't be opened
                            if (WidcommConnectionKey == null)
                                continue;

                            // Look for "ComPortNumber"
                            string ServiceGuid = WidcommConnectionKey.GetValue("GUID") as string;
                            int DevicePortNumber = Convert.ToInt32(WidcommConnectionKey.GetValue("ComPortNumber"));
                            object FriendlyNameValue = WidcommConnectionKey.GetValue("BDName");
                            string FriendlyName = null;

                            if (FriendlyNameValue != null)
                            {
#if !Framework10
                                if (Environment.OSVersion.Platform == PlatformID.WinCE)
                                {
                                    FriendlyName = FriendlyNameValue as string;
                                }
                                else
                                {
#endif
                                    byte[] FriendlyNameBytes = (byte[])FriendlyNameValue;
                                    FriendlyName = System.Text.ASCIIEncoding.ASCII.GetString(FriendlyNameBytes, 0, FriendlyNameBytes.Length).Replace("\0", "");
#if !Framework10
                                }
#endif
                            }

                            // If the service GUID is right, use it
                            if (ServiceGuid == "{00001101-0000-1000-8000-00805F9B34FB}")
                            {
#if PocketPC && !Framework20
									CommunicationPort DeviceComPort = CommunicationPort.Com0;
									switch(DevicePortNumber)
									{
										case 0:
											DeviceComPort = CommunicationPort.Com0;
											break;
										case 1:
											DeviceComPort = CommunicationPort.Com1;
											break;
										case 2:
											DeviceComPort = CommunicationPort.Com2;
											break;
										case 3:
											DeviceComPort = CommunicationPort.Com3;
											break;
										case 4:
											DeviceComPort = CommunicationPort.Com4;
											break;
										case 5:
											DeviceComPort = CommunicationPort.Com5;
											break;
										case 6:
											DeviceComPort = CommunicationPort.Com6;
											break;
										case 7:
											DeviceComPort = CommunicationPort.Com7;
											break;
										case 8:
											DeviceComPort = CommunicationPort.Com8;
											break;
										case 9:
											DeviceComPort = CommunicationPort.Com9;
											break;
										case 10:
											DeviceComPort = CommunicationPort.Com10;
											break;
										case 11:
											DeviceComPort = CommunicationPort.Com11;
											break;
										case 12:
											DeviceComPort = CommunicationPort.Com12;
											break;
										case 13:
											DeviceComPort = CommunicationPort.Com13;
											break;
										case 14:
											DeviceComPort = CommunicationPort.Com14;
											break;
										case 15:
											DeviceComPort = CommunicationPort.Com15;
											break;
										case 16:
											DeviceComPort = CommunicationPort.Com16;
											break;
										case 17:
											DeviceComPort = CommunicationPort.Com17;
											break;
										case 18:
											DeviceComPort = CommunicationPort.Com18;
											break;
										case 19:
											DeviceComPort = CommunicationPort.Com19;
											break;
										case 20:
											DeviceComPort = CommunicationPort.Com20;
											break;
										case 21:
											DeviceComPort = CommunicationPort.Com21;
											break;
										case 22:
											DeviceComPort = CommunicationPort.Com22;
											break;
										case 23:
											DeviceComPort = CommunicationPort.Com23;
											break;
										case 24:
											DeviceComPort = CommunicationPort.Com24;
											break;
										case 25:
											DeviceComPort = CommunicationPort.Com25;
											break;
										case 26:
											DeviceComPort = CommunicationPort.Com26;
											break;
										case 27:
											DeviceComPort = CommunicationPort.Com27;
											break;
										case 28:
											DeviceComPort = CommunicationPort.Com28;
											break;
										case 29:
											DeviceComPort = CommunicationPort.Com29;
											break;
										case 30:
											DeviceComPort = CommunicationPort.Com30;
											break;										
									}
#else
								// Set the port
                                CommunicationPort DeviceComPort = (CommunicationPort)Enum.Parse(typeof(CommunicationPort), DevicePortNumber.ToString(), false);
#endif

                                // Add the port to the list if it's not already in there
                                bool IsAlreadyRecorded = false;
                                lock (pSerialDevices.SyncRoot)
                                {
                                    // Try to find the device in the existing list
                                    foreach (SerialDevice device in pSerialDevices)
                                    {
                                        // Was it found?
                                        if (device.Port == DeviceComPort)
                                        {
                                            // Set the friendly name of the device
                                            //device.SetDeviceName(FriendlyName);

                                            // Yes.  We won't need to add it again
                                            IsAlreadyRecorded = true;
                                            break;
                                        }
                                    }
                                    // Was something found?
                                    if (!IsAlreadyRecorded)
                                    {
                                        // Yes.  Since it's a bluetooth device, we can max out the baud rate
                                        SerialDevice NewDevice = new SerialDevice(DeviceComPort, BaudRate.Baud115200, DataBits.Eight, Parity.None, StopBits.One, FlowControl.None, FriendlyName);
                                        // Set the friendly name of the device
                                        //NewDevice.SetDeviceName(FriendlyName);
                                        // And add it to the list
                                        pSerialDevices.Add(NewDevice);
                                    }
                                }
                                Thread.Sleep(0);
                                // And move on to the next key
                                break;
                            }
                        }
                        catch
                        {
                        }
                        finally
                        {
                            if (WidcommConnectionKey != null)
                                WidcommConnectionKey.Close();
                        }
                    }
                }
            }
            catch
            {
            }
            finally
            {
                if (Widcomm != null)
                {
                    Widcomm.Close();



                }
            }
			#endregion

			#region Microsoft Registry Analysis
            // Look for Microsoft Bluetooth devices
            RegistryKey Microsoft = null;
            try
            {
                Microsoft = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Bluetooth\Serial\Ports", false);
                if (Microsoft != null)
                {
                    // Get the default value
                    object PortValue = Microsoft.GetValue("(Default)");
                    if (PortValue != null)
                    {
                        string CommunicationPort = PortValue.ToString();
                        if (CommunicationPort.IndexOf("COM") == 0)
                        {
                            int Port = int.Parse(CommunicationPort.Substring(3, 1));
                            // Add the port to the list if it's not already in there
                            bool IsAlreadyRecorded = false;
                            lock (pSerialDevices.SyncRoot)
                            {
                                // Try to find the device in the existing list
                                foreach (SerialDevice device in pSerialDevices)
                                {
                                    // Was it found?
                                    if ((int)device.Port == Port)
                                    {
                                        // Yes.  We won't need to add it again
                                        IsAlreadyRecorded = true;
                                        break;
                                    }
                                }
                                // Was something found?
                                if (!IsAlreadyRecorded)
                                {
                                    SerialDevice NewDevice = new SerialDevice((CommunicationPort)Port, BaudRate.Baud57600);
                                    pSerialDevices.Add(NewDevice);
                                }
                            }
                            Thread.Sleep(0);
                        }
                    }
                }
            }
            catch
            {
                // Ignore registry errors
            }
            finally
            {
                if (Microsoft != null)
                {
                    Microsoft.Close();



                }
            }

			#endregion

			#region Registry Analysis
            // Now look for other possible areas
            RegistryKey Drivers = null;
            try
            {
                Drivers = Registry.LocalMachine.OpenSubKey(@"Drivers\Active", false);
                if (Drivers != null)
                {
                    // Analyze all sub-keys for COM ports
                    foreach (string subKey in Drivers.GetSubKeyNames())
                    {
                        string Key = null;
                        string Name = null;
                        // Look for a value called "Name"
                        RegistryKey SubKey = Drivers.OpenSubKey(subKey);
                        try
                        {
                            Key = SubKey.GetValue("Key") as string;
                            Name = SubKey.GetValue("Name") as string;
                        }
                        catch
                        {
                            continue;
                        }
                        finally
                        {
                            SubKey.Close();
                        }

                        // If there's no "Name" value, continue
                        if (Name == null)
                            continue;

                        //							// Skip some devices, such as "CIUART" and "SERIALUSB"
                        //							// because they will crash the device if opened!!!
                        // UPDATE: This has been fixed.  There was a threading
                        //    problem.  You can't call CreateFile many times at once.
                        //							if(Key == @"Drivers\BuiltIn\Serial"
                        //								|| Key.IndexOf("CIRUart") != -1
                        //								|| Key.IndexOf("SerialUSB") != -1
                        //								|| Key.IndexOf("BlueTooth") != -1
                        //								|| Key.IndexOf("IrCOMM") != -1
                        //								|| Key.IndexOf("IrDa") != -1)
                        //								continue;

                        // Look for "COM1:" values  "COM_:"
                        if (Name.IndexOf("COM") == -1)
                            continue;

                        // Extract the number from the string
                        try
                        {

                            int Port = int.Parse(Name.Substring(3).Replace(":", ""));

                            // Add the port to the list if it's not already in there
                            bool IsAlreadyRecorded = false;
                            lock (pSerialDevices.SyncRoot)
                            {
                                // Try to find the device in the existing list
                                foreach (SerialDevice device in pSerialDevices)
                                {
                                    // Was it found?
                                    if ((int)device.Port == Port)
                                    {
                                        // Yes.  We won't need to add it again
                                        IsAlreadyRecorded = true;
                                        break;
                                    }
                                }
                                // Was something found?
                                if (!IsAlreadyRecorded)
                                {
                                    //										if(Port != 1)
                                    //										{
                                    SerialDevice NewDevice = new SerialDevice((CommunicationPort)Port, BaudRate.Baud57600);
                                    pSerialDevices.Add(NewDevice);
                                    //										}
                                    //										else
                                    //										{
                                    //											Console.WriteLine("Key {0} {1}", Key, Name);
                                    //										}
                                }
                            }
                            Thread.Sleep(0);
                        }
                        catch
                        {
                            continue;
                        }
                    }
                }
            }
            catch
            {
            }
            finally
            {
                if (Drivers != null)
                {
                    Drivers.Close();
                }
            }
			#endregion
#endif


			// Whee!
			//GC.Collect();
			//GC.WaitForPendingFinalizers();

		}

		/// <summary>
		/// Returns a list of baud rates attempted during device detection.
		/// </summary>
		/// <remarks><para>During GPS device detection, serial devices may only respond with GPS
		/// data at certain baud rates.  As a result, GPS.NET may need to attempt multiple
		/// baud rates on the same device to retrieve valid data.  This collection controls 
		/// which baud rates are attempted.  The list should be kept as brief as possible 
		/// since each detection attempt requires about three seconds to complete.</para>
		/// <para>By default, this list contains baud rates 57600, 38400 and 4800.  Higher
		/// baud rates should exist earlier in the list in order to detect the
		/// fastest supported baud rate.</para>
		/// </remarks>
		public static ArrayList SerialBaudRates
		{
			get
			{
				return pSerialBaudRates;
			}
		}

        /// <summary>
        /// Prevents a specific port from being scanned during GPS device detection.
        /// </summary>
        /// <param name="port">A CommunicationPort value specifying a port to avoid.</param>
        /// <remarks>In some situations, automatic device detection could interfere with
        /// other serial devices connected to the system such as bar code scanners.  This
        /// method, when called before detection begins, will instruct GPS.NET to skip
        /// detection on the specified port.</remarks>
        public static void AvoidSerialPort(CommunicationPort port)
        {
            lock (pAvoidedSerialPorts.SyncRoot)
            {
                // If the port is already avoided, skip it
                if (pAvoidedSerialPorts.Contains(port))
                    return;

                // Add the port to the list
                pAvoidedSerialPorts.Add(port);
            }
        }

		/// <summary>
		/// Clears out all information about detected devices.
		/// </summary>
		/// <remarks>This method will clear out all cached information related to detected
		/// GPS devices.  This is typically called when GPS devices have changed on the system.
		/// For example, if a user has disconnected a GPS device and reattached a new device, the
		/// old device would no longer respond, even if it was previously detected.  This method
		/// allows "fresh" scans for GPS devices.</remarks>
		public static void ResetDevices()
		{
			// Stop any detection attempts in progress
			CancelDetection();

			// Now clear all device information
			lock (pSerialGpsDevices.SyncRoot)
			{
                foreach (Device item in pSerialGpsDevices)
                {
                    item.SetIsGpsDevice(false);
                }
				pSerialGpsDevices.Clear();
			}
			lock (pBluetoothGpsDevices.SyncRoot)
			{
                foreach (Device item in pBluetoothGpsDevices)
                {
                    item.SetIsGpsDevice(false);
                }
                pBluetoothGpsDevices.Clear();
			}
#if !PocketPC
            lock (pUsbGpsDevices.SyncRoot)
            {
                foreach (Device item in pUsbGpsDevices)
                {
                    item.SetIsGpsDevice(false);
                }
                pUsbGpsDevices.Clear();
            }
#endif
			lock (pGpsDevices.SyncRoot)
			{
                foreach (Device item in pGpsDevices)
                {
                    item.SetIsGpsDevice(false);
                }
				pGpsDevices.Clear();
			}

            // Set the last-known GPS device to null
            pLastUsedGpsDevice = null;

			// Finally, delete any cached device info
			try
			{
				RegistryKey Existing = Registry.CurrentUser.OpenSubKey(@"Software\GeoFrameworks\GPS.NET\2.0", true);
				if (Existing != null)
				{
					Existing.DeleteValue("Last Used Device");
					Existing.Close();
				}

				Existing = Registry.CurrentUser.OpenSubKey(@"Software\GeoFrameworks\GPS.NET\2.0\Serial Devices", false);
				if (Existing != null)
				{
					Existing.Close();
					Registry.CurrentUser.DeleteSubKeyTree(@"Software\GeoFrameworks\GPS.NET\2.0\Serial Devices");
				}

				Existing = Registry.CurrentUser.OpenSubKey(@"Software\GeoFrameworks\GPS.NET\2.0\Bluetooth Devices", false);
				if (Existing != null)
				{
					Existing.Close();
					Registry.CurrentUser.DeleteSubKeyTree(@"Software\GeoFrameworks\GPS.NET\2.0\Bluetooth Devices");
				}

				Existing = Registry.CurrentUser.OpenSubKey(@"Software\GeoFrameworks\GPS.NET\2.0\USB Devices", false);
				if (Existing != null)
				{
					Existing.Close();
					Registry.CurrentUser.DeleteSubKeyTree(@"Software\GeoFrameworks\GPS.NET\2.0\USB Devices");
				}
			}
			catch
			{
				// The key may not have existed.  Ignore
			}
		}

		/// <summary>
		/// Returns a list of serial ports installed on the computer.
		/// </summary>
		/// <value>An <strong>ArrayList</strong> containing settings for all installed
		/// serial devices.  The list may contain devices which are not GPS devices, such
		/// as a mouse or bar code scanner.  Devices with property installed USB-to-serial
		/// drivers will also appear in this list.</value>
#if !PocketPC
        [TypeConverter(typeof(ExpandableObjectConverter))]
#endif
		public static ArrayList SerialDevices
		{
			get
			{
				return pSerialDevices;
			}
		}

#if !PocketPC
        [TypeConverter(typeof(ExpandableObjectConverter))]
#endif
		public static ArrayList BluetoothDevices
		{
			get
			{
#if Smartphone
				if(pBluetoothDevices.Count == 0)
					RefreshBluetoothDevices();
#endif

				// Return enumerated bluetooth devices
				return pBluetoothDevices;
			}
		}

#if !PocketPC
        [TypeConverter(typeof(ExpandableObjectConverter))]
        public static ArrayList UsbDevices
        {
            get
            {               
                return pUsbDevices;
            }
        }

        [TypeConverter(typeof(ExpandableObjectConverter))]
        public static ArrayList UsbGpsDevices
        {
            get
            {
                return pUsbGpsDevices;
            }
        }
#endif

		[Obsolete("Use \"Devices.GetDeviceByProtocol(\"NMEA-0183\")\" instead of \"Devices.FirstRespondingGpsDevice\".  This was changed to provide better support for more than one GPS data protocol.")]
		public static Device FirstRespondingGpsDevice
		{
			get
			{
				return GetDeviceByProtocol("NMEA-0183");
			}
		}

        public static Device FirstRespondingNmeaDevice
        {
            get
            {
                return GetDeviceByProtocol("NMEA-0183");
            }
        }

		public static Stream GetStreamByProtocol(string protocol)
		{
			// Get the device for this protocol
			Device Result = GetDeviceByProtocol(protocol);

			// Was a device found?
			if (Result != null)
				// Yes.  Return its stream
				return Result.GetHardwareStream();
			else
#if PocketPC
#if Smartphone
#if Framework20
                throw new IOException("No GPS device could be found matching the requested \"" + protocol + "\" protocol.  Please check to make sure that Bluetooth is enabled.  GPS devices should be paired via Bluetooth Settings. An Outgoing COM Port should also be configured to take advantage of GPS device sharing.");
#else
				throw new IOException("No GPS device could be found matching the requested \"" + protocol + "\" protocol.  Please check to make sure Bluetooth GPS devices are currently powered on.");
#endif
#elif WindowsCE
				throw new IOException("No GPS device could be found matching the requested \"" + protocol + "\" protocol.  Please check to make sure a GPS device is powered on and connected to your PDA.");
#else
				throw new IOException("No GPS device could be found matching the requested \"" + protocol + "\" protocol.  Please check to make sure GPS devices are powered on and paired via the Bluetooth Manager.");
#endif
#else
                throw new IOException("No GPS device could be found matching the requested \"" + protocol + "\" protocol.  Please check to make sure GPS devices are powered on and connected to the computer.  Some newer USB devices require special USB-to-serial drivers before they will be recognized by the system.");
#endif
		}

		public static Device GetDeviceByProtocol(string protocol)
		{
			// Look for a device matching the protocol
			if (pGpsDevices.Count > 0)
			{
				lock (pGpsDevices.SyncRoot)
				{
#if PocketPC
					// Look for a shared GPS driver first
					if (pAllowSharedGpsDeviceConnections && SharedGpsDevice.IsSupported && pSharedGpsDevice.IsEnabled)
					{
						foreach (Device device in pGpsDevices)
						{
							if (device is SharedGpsDevice && device.Protocol == protocol)
                            {
							    pLastUsedGpsDevice = device;
								return device;
                            }
						}
					}
#endif

                    // First, see if a device has an open stream available
					foreach (Device device in pGpsDevices)
					{
						if (device.Protocol == protocol && device.IsImmediateNeedStreamAvailable)
						{
							pLastUsedGpsDevice = device;
							return device;
						}
					}
					// Next, find any device with an available stream
					foreach (Device device in pGpsDevices)
					{
						if (device.Protocol == protocol)
						{
							pLastUsedGpsDevice = device;
							return device;
						}
					}
				}
				Thread.Sleep(0);
			}

			// Signal that we need a connection ASAP
			pIsDeviceNeededImmediately = true;

			// Start device detection to try and pick something up
			Detect();

			// Now loop until we find a device matching the right protocol
			while (pIsDetectionInProgress)
			{
				lock (pGpsDevices.SyncRoot)
				{
#if PocketPC
					// Look for a shared GPS driver first
					if (pAllowSharedGpsDeviceConnections && SharedGpsDevice.IsSupported)
					{
						foreach (Device device in pGpsDevices)
						{
							if (device is SharedGpsDevice && device.Protocol == protocol)
							{
								pIsDeviceNeededImmediately = false;
							    pLastUsedGpsDevice = device;
								return device;
							}
						}
					}
#endif

                    // First, see if a device has an open stream available
					foreach (Device device in pGpsDevices)
					{
						if (device.Protocol == protocol && device.IsImmediateNeedStreamAvailable)
						{
							// Signal that we no longer need a connection ASAP
							pIsDeviceNeededImmediately = false;
							pLastUsedGpsDevice = device;
							return device;
						}
					}
					// Next, find any device with an available stream
					foreach (Device device in pGpsDevices)
					{
						if (device.Protocol == protocol)
						{
							pIsDeviceNeededImmediately = false;
							pLastUsedGpsDevice = device;
							return device;
						}
					}
				}
				Thread.Sleep(100);
				Thread.Sleep(0);

				// Let the host keep processing events
				System.Windows.Forms.Application.DoEvents();
			}

			// Look for a device matching the protocol one last time
			if (pGpsDevices.Count > 0)
			{
				lock (pGpsDevices.SyncRoot)
				{
#if PocketPC
					// Look for a shared GPS driver first
					if (pAllowSharedGpsDeviceConnections && SharedGpsDevice.IsSupported)
					{
						foreach (Device device in pGpsDevices)
						{
							if (device is SharedGpsDevice && device.Protocol == protocol)
							{
								pIsDeviceNeededImmediately = false;
								return device;
							}
						}
					}
#endif

					// First, see if a device has an open stream available
					foreach (Device device in pGpsDevices)
					{
						if (device.Protocol == protocol && device.IsImmediateNeedStreamAvailable)
						{
							// Signal that we no longer need a connection ASAP
							pIsDeviceNeededImmediately = false;
							pLastUsedGpsDevice = device;
							return device;
						}
					}
					// Next, find any device with an available stream
					foreach (Device device in pGpsDevices)
					{
						if (device.Protocol == protocol)
						{
							pIsDeviceNeededImmediately = false;
							pLastUsedGpsDevice = device;
							return device;
						}
					}
				}
			}
			Thread.Sleep(0);

			// Nothing was found :(
			return null;
		}

		/// <summary>Returns the Device object associated with the specified stream.</summary>
#if !PocketPC
        [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode=true)]
#endif
		public static Device GetDeviceFromStream(Stream stream)
		{
			// If this is a GpsStream, we have to dig deeper to its underlying stream
			GpsStream TargetAsAutomatic = stream as GpsStream;
			SerialStream TargetAsSerial = stream as SerialStream;
			BluetoothStream TargetAsBluetooth = stream as BluetoothStream;
			Emulator TargetAsEmulator = stream as Emulator;

			// If this is an automatic stream, analyze the base stream
			if (TargetAsAutomatic != null)
			{
				return GetDeviceFromStream(TargetAsAutomatic.BaseStream);
			}
				// Is this a serial device?
			else if (TargetAsSerial != null)
			{
				lock (pSerialDevices.SyncRoot)
				{
					// Look for a device whose hardware stream matches the specified one
					foreach (SerialDevice device in pSerialDevices)
					{
						if (device.Port == TargetAsSerial.Port)
							return device;
					}
				}
			}
			else if (TargetAsBluetooth != null)
			{
				lock (pBluetoothDevices.SyncRoot)
				{
					foreach (BluetoothDevice device in pBluetoothDevices)
					{
						if (device.Address.Equals(TargetAsBluetooth.Address))
							return device;
					}
				}
			}
			else if (TargetAsEmulator != null)
			{
				lock (pVirtualGpsDevices.SyncRoot)
				{
					foreach (VirtualDevice device in pVirtualGpsDevices)
					{
						if (device.Emulator.GetType().Equals(TargetAsEmulator.GetType()))
							return device;
					}
				}
			}

			// No device was found, so exit
			return null;
		}

		///// <summary>
		///// Removes a GPS device from the list of detected devices based on it's raw data stream.
		///// </summary>
		///// <param name="sender">The object which requested unregistration.</param>
		///// <param name="hardwareStream">A <strong>Stream</strong> to a GPS device which has stopped responding.</param>
		///// <remarks>This method is used to signal that a GPS device has stopped responding.  Devices
		///// may stop responding if they lose power or go out of range.  This methods attempts to
		///// match the specified Stream to the list of registered GPS devices, and if successful
		///// will remove the device.</remarks>
		//public static void RegisterDeviceFailure(Stream hardwareStream)
		//{
		//    RegisterDeviceFailure(GetDeviceFromStream(hardwareStream));
		//}

		///// <summary>
		///// Removes a GPS device from the list of detected devices.
		///// </summary>
		///// <param name="device">The GPS device to remove.</param>
		///// <remarks>A GPS device can suddenly stop responding if it
		///// loses power or goes out of range.  In these situations, it
		///// may be necessary to engage a fall-back GPS device in order
		///// to maintain the flow of GPS data.  When a connection to
		///// a device is repeatedly unsuccessful, this method is called
		///// in order to signal that the device is no longer available.
		///// Automatic device detection will be performed on subsequent
		///// connection attempts to try and locate a second GPS device.</remarks>
		//public static void RegisterDeviceFailure(Device device)
		//{
		//    // if the device is null, return
		//    if (device == null)
		//        throw new ArgumentNullException("device", "A device object is required when registering a device failure.  It cannot be null.");
		//    // Bump up the failure count
		//    //device.FailureCount++;
		//    // Allow one failure before unregistering the device
		//    if (device.FailureCount <= 1)
		//        return;
		//    // Remove the device from any existing collections
		//    if (pGpsDevices.Contains(device))
		//    {
		//        lock (pGpsDevices.SyncRoot)
		//        {
		//            pGpsDevices.Remove(device);
		//        }
		//    }
		//    if (pSerialGpsDevices.Contains(device))
		//    {
		//        lock (pSerialGpsDevices.SyncRoot)
		//        {
		//            pSerialGpsDevices.Remove(device);
		//        }
		//    }
		//    // Flag that the device is no longer responding.
		//    OnDeviceFailure(device);
		//}

		/// <summary>Indicates if device detection is currently in progress.</summary>
		public static bool IsDetectionInProgress
		{
			get
			{
				return pIsDetectionInProgress;
			}
		}

#if !Smartphone
        /// <summary>
        /// Controls whether devices are scanned even if there is no evidence they exists at all.
        /// </summary>
        /// <remarks>Some GPS device manufacturers will release GPS devices which require special drivers
        /// in order to work.  Well-written drivers will write an entry in the system registry which
        /// identifies the existance of the device.  Some drivers, however, do not do this and therefore
        /// might be skipped during Automatic Device Detection.  Enabling this property instructs GPS.NET
        /// to attempt all ports between (and including)  COM0: to COM20: even if registry entries for
        /// these ports do not exist.  While enabling this property can decrease performance, the penalty
        /// is slight and it is acceptable in order to have a greater chance of discovering a GPS Device.</remarks>
        public static bool IsExhaustiveDetectionEnabled
        {
            get
            {
                return pIsExhaustiveDetectionEnabled;
            }
            set
            {
                pIsExhaustiveDetectionEnabled = value;
            }
        }

        /// <summary>
        /// Controls whether device detection is stopped after detecting the first device.
        /// </summary>
        /// <remarks>Some applications will only ever need to connect to one GPS device.  In such situations,
        /// a performance win can be gained by aborting device detection as soon as one device is detected.  When
        /// this property is <strong>True</strong>, device detection is immediately stopped as soon as one GPS device
        /// is discovered.  Otherwise, detection continues to locate additional GPS devices.</remarks>
        public static bool IsOnlyFirstDeviceDetected
        {
            get
            {
                return pIsOnlyFirstDeviceDetected;
            }
            set
            {
                pIsOnlyFirstDeviceDetected = value;
            }
        }
#endif


		/// <summary>
		/// Controls whether the local machine's clock is synchronized with the GPS receiver.
		/// </summary>
		/// <remarks>This property is typically used to correct the local machine's
		/// system clock based on the date and time obtained from GPS satellites.
		/// Synchronizing the clock is generally a good idea so long as a satellite fix
		/// is in progress.  When this property is <strong>True</strong>, the system
		/// clock will be set whenever the "CurrentUtcDateTime" property changes,
		/// and the "CurrentlyFixed" property is <strong>True</strong>.</remarks>
		public static bool IsClockSynchronizationEnabled
		{
			get
			{
				return pIsClockSynchronizationEnabled;
			}
			set
			{
				pIsClockSynchronizationEnabled = value;
			}
		}

		/// <summary>Indicates if device detection has successfully completed.</summary>
		public static bool IsDetectionComplete
		{
			get
			{
				return pIsDetectionComplete;
			}
		}

		/// <summary>Indicates if any GPS device has been discovered.</summary>
		public static bool IsDeviceDetected
		{
			get
			{
				return pGpsDevices.Count > 0;
			}
		}

		/// <summary>Indicates if a connection is waiting for a GPS device to be disocvered.</summary>
		public static bool IsDeviceNeededImmediately
		{
			get
			{
				return pIsDeviceNeededImmediately;
			}
		}

		///// <summary>Returns the first device to be identified as a GPS device.</summary>
		//public static Device FirstRespondingGpsStream
		//{
		//    get
		//    {
		//        // Has a device already been detected?
		//        if (pGpsDevices.Count == 0)
		//        {
		//            // No.  Do detection and wait for 
		//        }
		//        // Signal that we're waiting for a stream
		//        pIsDeviceNeededImmediately = true;

		//        // Do we already have devices?
		//        if (pFirstRespondingGpsDevice == null)
		//        {
		//            // No.  Start detection if it's not already going
		//            if (!pIsDetectionInProgress && pGpsDevices.Count == 0)
		//                Detect();

		//            // Wait for device detection to return
		//            DeviceDetectedWaitHandle.WaitOne();
		//        }

		//        return pFirstRespondingGpsDevice;
		//    }
		//}


		///// <summary>Returns the first device to be identified as a GPS device.</summary>
		//public static Device FirstRespondingGpsDevice
		//{
		//    get
		//    {
		//        // Signal that we're waiting for a stream
		//        pIsDeviceNeededImmediately = true;

		//        // Do we already have devices?
		//        if (pFirstRespondingGpsDevice == null)
		//        {
		//            // No.  Start detection if it's not already going
		//            if (!pIsDetectionInProgress && pGpsDevices.Count == 0)
		//                Detect();

		//            // Wait for device detection to return
		//            DeviceDetectedWaitHandle.WaitOne();
		//        }

		//        return pFirstRespondingGpsDevice;
		//    }
		//}

		/// <summary>
		/// Blocks execution until a device detection completes, or the default timeout occurs. 
		/// </summary>
		/// <returns></returns>
		public static bool WaitForDetection()
		{
			return WaitForDetection(pDeviceDetectionTimeout);
		}

		/// <summary>
		/// Blocks execution until a device detection completes or the specified timeout period elapses. 
		/// </summary>
		/// <returns></returns>
		public static bool WaitForDetection(TimeSpan timeout)
		{
#if PocketPC
			int Duration = Convert.ToInt32(timeout.TotalMilliseconds / 100);
			for (int i = 0; i < Duration; i++)
			{
				if (!pIsDetectionInProgress)
					return true;

				Thread.Sleep(100);
				Thread.Sleep(0);
			}
			return false;
#else
            return DetectionWaitHandle.WaitOne(timeout, false);
#endif
		}

        /// <summary>
        /// Blocks execution until any GPS device has been discovered, detection is completed, or the default timeout occurs. 
        /// </summary>
        /// <returns>A Boolean value, <strong>True</strong> if a GPS device has been detected.</returns>
        /// <remarks>This method is helpful for single-threaded applications which need to wait until a
        /// GPS device is confirmed before performing any GPS tasks.  Typically, multi-threaded
        /// applications will issue a call to the Detect method and not wait for detection to complete because
        /// it completes automatically in the background.</remarks>
        public static bool WaitForAnyDevice()
        {
            return WaitForAnyDevice(pDeviceDetectionTimeout);
        }

        /// <summary>
        /// Blocks execution until any GPS device has been discovered, detection is completed, or the default timeout occurs. 
        /// </summary>
        /// <returns>A Boolean value, <strong>True</strong> if a GPS device has been detected.</returns>
        /// <remarks>This method is helpful for single-threaded applications which need to wait until a
        /// GPS device is confirmed before performing any GPS tasks.  Typically, multi-threaded
        /// applications will issue a call to the Detect method and not wait for detection to complete because
        /// it completes automatically in the background.</remarks>
        public static bool WaitForAnyDevice(TimeSpan timeout)
        {
#if PocketPC
			int Duration = Convert.ToInt32(timeout.TotalMilliseconds / 100);
			for (int i = 0; i < Duration; i++)
			{
				if (!pIsDetectionInProgress || pGpsDevices.Count > 1)
					return true;

				Thread.Sleep(100);
				Thread.Sleep(0);
			}
			return false;
#else
            return DeviceDetectedWaitHandle.WaitOne(timeout, false);
#endif
        }

		/// <summary>
		/// Returns a list of serial devices which have been identified as GPS
		/// devices.
		/// </summary>
#if !PocketPC
        [TypeConverter(typeof(ExpandableObjectConverter))]
#endif
		public static ArrayList SerialGpsDevices
		{
			get
			{
				// Start detection if it's not already going
				if (!IsDetectionInProgress && pGpsDevices.Count == 0)
					Detect();

				// Wait for detection to complete
				WaitForDetection();

				return pSerialGpsDevices;
			}
		}

		/// <summary>
		/// Returns a list of virtual GPS devices which have been registered.
		/// </summary>
#if !PocketPC
        [TypeConverter(typeof(ExpandableObjectConverter))]
#endif
		public static ArrayList VirtualGpsDevices
		{
			get
			{
				return pVirtualGpsDevices;
			}
		}

		/// <summary>
		/// Returns a list of Bluetooth™ devices which have been identified as GPS
		/// devices.
		/// </summary>
#if !PocketPC
        [TypeConverter(typeof(ExpandableObjectConverter))]
#endif
		public static ArrayList BluetoothGpsDevices
		{
			get
			{
				// Start detection if it's not already going
				if (!IsDetectionInProgress && pGpsDevices.Count == 0)
					Detect();

				// Wait for detection to complete
				WaitForDetection();

				return pBluetoothGpsDevices;
			}
		}

		public static CommunicationPort BluetoothVirtualSerialPort
		{
			get
			{
				return pBluetoothVirtualSerialPort;
			}
		}

		/// <summary>Returns a list of recognized GPS devices.</summary>
#if !PocketPC
        [TypeConverter(typeof(ExpandableObjectConverter))]
#endif
		public static ArrayList GpsDevices
		{
			get
			{
				// Start detection if it's not already going
				if (!IsDetectionInProgress && pGpsDevices.Count == 0)
					Detect();

				// Wait for detection to complete
				WaitForDetection();

				return pGpsDevices;
			}
		}

		//#if !PocketPC
		//		private static void Application_ApplicationExit(object sender, EventArgs e)
		//		{
		//			try
		//			{
		//				// Cancel any detection in progress
		//				CancelDetection();
		//			}
		//			catch
		//			{
		//			}
		//
		//			// Close any open wait handles
		//			try
		//			{
		//				DetectionWaitHandle.Close();
		//			}
		//			catch 
		//			{
		//			}
		//
		//			try
		//			{
		//				DeviceDetectedWaitHandle.Close();
		//			}
		//			catch
		//			{
		//			}
		//		}
        //#endif

        #endregion

    }

	public delegate void DeviceEventHandler(object sender, DeviceEventArgs e);

	internal sealed class DevicesShutdownSignal
	{
		public DevicesShutdownSignal() { }

		~DevicesShutdownSignal()
		{
			Devices.Dispose();
		}
	}
}
